Skip to content

Helpers for constructing JNI signatures #770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dcharkes opened this issue Sep 9, 2022 · 7 comments
Open

Helpers for constructing JNI signatures #770

dcharkes opened this issue Sep 9, 2022 · 7 comments

Comments

@dcharkes
Copy link
Collaborator

dcharkes commented Sep 9, 2022

To construct strings such as "I(I)" we could add some helper code that could construct them from Dart function types.

  String typeToJavaSignature<T extends Function>() {
    final splitOnArrow = T.toString().split(' => ');
    final returnType = splitOnArrow[1];
    final arguments = splitOnArrow[0];
    final argumentsTrimmed = arguments.substring(1, arguments.length - 1);
    final argumentTypes = argumentsTrimmed.split(', ');
    final javaReturn = dartTypeToJavaSignatureType[returnType]!;
    final javaArguments =
        argumentTypes.map((e) => dartTypeToJavaSignatureType[e]!).join(',');
    return '($javaArguments)$javaReturn';
  }
final javaSignature = typeToJavaSignature<int Function(int)>();

The is probably not general enough, but maybe it would be a nice addition to the jni interface over having users having to construct the strings themselves.

@dcharkes
Copy link
Collaborator Author

dcharkes commented Sep 9, 2022

This probably cannot be fully shared with the logic in jnigen, because there we generate from the serialized Java type. But the logic in there should be similar to logic of doing it with Dart signatures.

We could consider jnigen generating Dart function types representing the signatures, that would make the API a bit more typed. Unfortunately we cannot change T callStaticMethodByName<T> to T callStaticMethodByName<T, S extends T Function(...)> constraining the return type and also not constraining the argument types which are List<dynamic>. However, we could runtime check the arguments based on the signature if we do T callStaticMethodByName<T, S extends Function>.

@mahesh-hegde
Copy link
Contributor

There are few practical difficulties in using function types. First, signatures use qualified names, which don't appear in string representation of pointer type. Second, one primitive type in dart may map to multiple types in java. (Int -> byte, short, int, long). There maybe more I am forgetting. So unless you're trying to get signature for simplest methods using int, float, string etc.. it won't work.

I also thought of inferring it from arguments passed. But that doesn't work because the actual type that function defines may be a superclass. But that may be a good default.

@dcharkes
Copy link
Collaborator Author

dcharkes commented Sep 9, 2022

Second, one primitive type in dart may map to multiple types in java.

In dart:ffi we work around that by introducing type markers for C types Int8 Function(Int8).

signatures use qualified names

class Foo {}

const Map<Type, String> _fullyQualifiedNames = {
  Foo: "com.example.Foo",
};

We could generate that in jnigen, but that map needs to be set up by the user if using package:jni by hand. That would lead to registerFullyQualifiedName(Type type, String fullyQualifiedName) (or a registerFullyQualifiedNames variant that takes more than one).

which don't appear in string representation of pointer type

We might be able to get the type out of a pointer by:

extension PointerArgType<T extends NativeType> on Pointer<T>{
  Type get argType => T;
}

And then use a Type t argument instead of a T extends Function type argument:

  String typeToJavaSignature(Type type) {
    final splitOnArrow = type.toString().split(' => ');
    final returnType = splitOnArrow[1];
    final arguments = splitOnArrow[0];
    final argumentsTrimmed = arguments.substring(1, arguments.length - 1);
    final argumentTypes = argumentsTrimmed.split(', ');
    final javaReturn = dartTypeToJavaSignatureType[returnType]!;
    final javaArguments =
        argumentTypes.map((e) => dartTypeToJavaSignatureType[e]!).join(',');
    return '($javaArguments)$javaReturn';
  }

But lets shelf this idea for now.

@dcharkes
Copy link
Collaborator Author

dcharkes commented Nov 4, 2022

One other solution that comes to mind is to parse T.toString() and construct the Java signature from that.

I strongly discourage use of T.toString(). It's only supposed to be used as a debugging aid and can change (e.g. release build can be returning some mangled string due to obfuscation).

_Originally posted by @mraleph in #720

@HosseinYousefi
Copy link
Member

One other solution that comes to mind is to parse T.toString() and construct the Java signature from that.

I strongly discourage use of T.toString(). It's only supposed to be used as a debugging aid and can change (e.g. release build can be returning some mangled string due to obfuscation).

Originally posted by @mraleph in #120 (comment)

We can safely close this issue then?

@dcharkes
Copy link
Collaborator Author

dcharkes commented Nov 4, 2022

Well, we still would like these helpers (if anywhere in the jni or jnigen api users have to ever provide these signatures manually). We should just not construct them from types.

@HosseinYousefi
Copy link
Member

HosseinYousefi commented Nov 4, 2022

Well, we still would like these helpers (if anywhere in the jni or jnigen api users have to ever provide these signatures manually). We should just not construct them from types.

Using the same comment, maybe for a function like String concat(String[] arr, bool space) we could get the signature like getFunctionSig(args: [JniString.klass.arrayKlass, JniBoolean.klass], ret: JniString.klass)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Backlog
Development

No branches or pull requests

3 participants