Skip to content

Commit

Permalink
Use varargs for dartonly method calls (#1090)
Browse files Browse the repository at this point in the history
  • Loading branch information
HosseinYousefi authored Apr 17, 2024
1 parent ffc493d commit e88a6a8
Show file tree
Hide file tree
Showing 20 changed files with 7,146 additions and 1,526 deletions.
2 changes: 1 addition & 1 deletion pkgs/jni/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.8.0-wip"
version: "0.8.0"
js:
dependency: transitive
description:
Expand Down
4 changes: 4 additions & 0 deletions pkgs/jni/ffigen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ functions:
- 'GetJniContextPtr'
- 'setJniGetters'
- 'jni_log'
# Exclude functions with VarArgs, jnigen will generate them based on the
# exact arguments needed.
- 'globalEnv_NewObject'
- 'globalEnv_Call(Static|Nonvirtual|)[A-Z][a-z]+Method'
# Inline functions
# keep-sorted start
- 'acquire_lock'
Expand Down
3 changes: 3 additions & 0 deletions pkgs/jni/lib/src/jni.dart
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@ extension ProtectedJniExtensions on Jni {
Jni._ensureInitialized();
Jni._bindings.deleteFinalizableHandle(finalizableHandle, object);
}

static Pointer<T> Function<T extends NativeType>(String) get lookup =>
Jni._dylib.lookup;
}

extension AdditionalEnvMethods on GlobalJniEnv {
Expand Down
331 changes: 246 additions & 85 deletions pkgs/jni/src/third_party/global_jni_env.c

Large diffs are not rendered by default.

184 changes: 153 additions & 31 deletions pkgs/jni/src/third_party/global_jni_env.h

Large diffs are not rendered by default.

75 changes: 56 additions & 19 deletions pkgs/jni/tool/wrapper_generators/generate_c_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ const wrapperGetterDecl = '''
FFI_PLUGIN_EXPORT $wrapperName* GetGlobalEnv();
''';

bool hasVarArgs(String name) {
return name == 'NewObject' ||
RegExp(r'^Call(Static|Nonvirtual|)[A-Z][a-z]+Method$').hasMatch(name);
}

/// Get C name of a type from its ffigen representation.
String getCType(Type type) {
if (type is PointerType) {
Expand All @@ -81,19 +86,24 @@ FunctionType getGlobalJniEnvFunctionType(FunctionType ft) {
}

// Returns declaration of function field in GlobalJniEnv struct
String getFunctionFieldDecl(
Member field,
) {
String getFunctionFieldDecl(Member field, {required bool isField}) {
final fieldType = field.type;
if (fieldType is PointerType && fieldType.child is NativeFunc) {
final nativeFunc = fieldType.child as NativeFunc;
final functionType = getGlobalJniEnvFunctionType(nativeFunc.type);
final resultWrapper = getResultWrapper(getCType(functionType.returnType));
final name = field.name;
final withVarArgs = hasVarArgs(name);
final params = functionType.parameters
.map((param) => '${getCType(param.type)} ${param.name}')
.join(', ');
return ('${resultWrapper.returnType} (*$name)($params);');
.map((param) => '${getCType(param.type)} ${param.name}')
.join(', ') +
(withVarArgs ? ', ...' : '');
final willExport = withVarArgs ? 'FFI_PLUGIN_EXPORT ' : '';
if (isField) {
return '${resultWrapper.returnType} (*$name)($params);';
}
return '$willExport${resultWrapper.returnType} '
'${getWrapperFuncName(field)}($params);';
} else {
return 'void* ${field.name};';
}
Expand Down Expand Up @@ -222,32 +232,50 @@ String? getWrapperFunc(Member field) {
final outerFunctionType = getGlobalJniEnvFunctionType(functionType);
final wrapperName = getWrapperFuncName(field);
final returnType = getCType(outerFunctionType.returnType);
final params = outerFunctionType.parameters
.map((param) => '${getCType(param.type)} ${param.name}')
.join(', ');
final withVarArgs = hasVarArgs(field.name);
final params = [
...outerFunctionType.parameters
.map((param) => '${getCType(param.type)} ${param.name}'),
if (withVarArgs) '...',
].join(', ');
var returnCapture = returnType == 'void' ? '' : '$returnType $resultVar =';
if (constBufferReturningFunctions.contains(field.name)) {
returnCapture = 'const $returnCapture';
}
final callParams = [
'jniEnv',
...(outerFunctionType.parameters.map((param) => param.name).toList())
...(outerFunctionType.parameters.map((param) => param.name).toList()),
if (withVarArgs) 'args',
].join(', ');
final resultWrapper = getResultWrapper(returnType);

var convertRef = '';
if (isJRefType(returnType) && !refFunctions.contains(field.name)) {
convertRef = ' $resultVar = to_global_ref($resultVar);\n';
}
final callee = field.name + (withVarArgs ? 'V' : '');
final varArgsInit = withVarArgs
? '''
va_list args;
va_start(args, methodID);
'''
: '';
final varArgsEnd = withVarArgs ? 'va_end(args);\n' : '';
final exceptionCheck = _noCheckException.contains(field.name)
? ''
: ' jthrowable $errorVar = check_exception();\n'
' if ($errorVar != NULL) {\n'
' return ${resultWrapper.onError};\n'
' }\n';
return '${resultWrapper.returnType} $wrapperName($params) {\n'
: '''
jthrowable $errorVar = check_exception();
if ($errorVar != NULL) {
return ${resultWrapper.onError};
}
''';
final willExport = withVarArgs ? 'FFI_PLUGIN_EXPORT ' : '';
return '$willExport'
'${resultWrapper.returnType} $wrapperName($params) {\n'
' attach_thread();\n'
' $returnCapture (*jniEnv)->${field.name}($callParams);\n'
'$varArgsInit'
' $returnCapture (*jniEnv)->$callee($callParams);\n'
'$varArgsEnd'
'$exceptionCheck'
'$convertRef'
' return ${resultWrapper.onResult};\n'
Expand All @@ -259,11 +287,20 @@ String? getWrapperFunc(Member field) {
void writeGlobalJniEnvWrapper(Library library) {
final jniEnvType = findCompound(library, envType);

final fieldDecls = jniEnvType.members.map(getFunctionFieldDecl).join('\n');
final fieldDecls = jniEnvType.members
.map((member) => getFunctionFieldDecl(member, isField: true))
.join('\n');
final varArgsFunctions = jniEnvType.members
.where((member) => hasVarArgs(member.name))
.map((member) => getFunctionFieldDecl(member, isField: false))
.join('\n');
final structDecl =
'typedef struct $wrapperName {\n$fieldDecls\n} $wrapperName;\n';
File.fromUri(Paths.globalJniEnvH).writeAsStringSync(
'$preamble$wrapperDeclIncludes$structDecl$wrapperGetterDecl');
File.fromUri(Paths.globalJniEnvH).writeAsStringSync('$preamble'
'$wrapperDeclIncludes'
'$structDecl'
'$wrapperGetterDecl'
'$varArgsFunctions\n');

final functionWrappers = StringBuffer();
final structInst = StringBuffer('$wrapperName globalJniEnv = {\n');
Expand Down
Loading

0 comments on commit e88a6a8

Please sign in to comment.