Skip to content

Commit e88a6a8

Browse files
Use varargs for dartonly method calls (#1090)
1 parent ffc493d commit e88a6a8

File tree

20 files changed

+7146
-1526
lines changed

20 files changed

+7146
-1526
lines changed

pkgs/jni/example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ packages:
200200
path: ".."
201201
relative: true
202202
source: path
203-
version: "0.8.0-wip"
203+
version: "0.8.0"
204204
js:
205205
dependency: transitive
206206
description:

pkgs/jni/ffigen.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ functions:
3434
- 'GetJniContextPtr'
3535
- 'setJniGetters'
3636
- 'jni_log'
37+
# Exclude functions with VarArgs, jnigen will generate them based on the
38+
# exact arguments needed.
39+
- 'globalEnv_NewObject'
40+
- 'globalEnv_Call(Static|Nonvirtual|)[A-Z][a-z]+Method'
3741
# Inline functions
3842
# keep-sorted start
3943
- 'acquire_lock'

pkgs/jni/lib/src/jni.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ extension ProtectedJniExtensions on Jni {
300300
Jni._ensureInitialized();
301301
Jni._bindings.deleteFinalizableHandle(finalizableHandle, object);
302302
}
303+
304+
static Pointer<T> Function<T extends NativeType>(String) get lookup =>
305+
Jni._dylib.lookup;
303306
}
304307

305308
extension AdditionalEnvMethods on GlobalJniEnv {

pkgs/jni/src/third_party/global_jni_env.c

Lines changed: 246 additions & 85 deletions
Large diffs are not rendered by default.

pkgs/jni/src/third_party/global_jni_env.h

Lines changed: 153 additions & 31 deletions
Large diffs are not rendered by default.

pkgs/jni/tool/wrapper_generators/generate_c_extensions.dart

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ const wrapperGetterDecl = '''
5656
FFI_PLUGIN_EXPORT $wrapperName* GetGlobalEnv();
5757
''';
5858

59+
bool hasVarArgs(String name) {
60+
return name == 'NewObject' ||
61+
RegExp(r'^Call(Static|Nonvirtual|)[A-Z][a-z]+Method$').hasMatch(name);
62+
}
63+
5964
/// Get C name of a type from its ffigen representation.
6065
String getCType(Type type) {
6166
if (type is PointerType) {
@@ -81,19 +86,24 @@ FunctionType getGlobalJniEnvFunctionType(FunctionType ft) {
8186
}
8287

8388
// Returns declaration of function field in GlobalJniEnv struct
84-
String getFunctionFieldDecl(
85-
Member field,
86-
) {
89+
String getFunctionFieldDecl(Member field, {required bool isField}) {
8790
final fieldType = field.type;
8891
if (fieldType is PointerType && fieldType.child is NativeFunc) {
8992
final nativeFunc = fieldType.child as NativeFunc;
9093
final functionType = getGlobalJniEnvFunctionType(nativeFunc.type);
9194
final resultWrapper = getResultWrapper(getCType(functionType.returnType));
9295
final name = field.name;
96+
final withVarArgs = hasVarArgs(name);
9397
final params = functionType.parameters
94-
.map((param) => '${getCType(param.type)} ${param.name}')
95-
.join(', ');
96-
return ('${resultWrapper.returnType} (*$name)($params);');
98+
.map((param) => '${getCType(param.type)} ${param.name}')
99+
.join(', ') +
100+
(withVarArgs ? ', ...' : '');
101+
final willExport = withVarArgs ? 'FFI_PLUGIN_EXPORT ' : '';
102+
if (isField) {
103+
return '${resultWrapper.returnType} (*$name)($params);';
104+
}
105+
return '$willExport${resultWrapper.returnType} '
106+
'${getWrapperFuncName(field)}($params);';
97107
} else {
98108
return 'void* ${field.name};';
99109
}
@@ -222,32 +232,50 @@ String? getWrapperFunc(Member field) {
222232
final outerFunctionType = getGlobalJniEnvFunctionType(functionType);
223233
final wrapperName = getWrapperFuncName(field);
224234
final returnType = getCType(outerFunctionType.returnType);
225-
final params = outerFunctionType.parameters
226-
.map((param) => '${getCType(param.type)} ${param.name}')
227-
.join(', ');
235+
final withVarArgs = hasVarArgs(field.name);
236+
final params = [
237+
...outerFunctionType.parameters
238+
.map((param) => '${getCType(param.type)} ${param.name}'),
239+
if (withVarArgs) '...',
240+
].join(', ');
228241
var returnCapture = returnType == 'void' ? '' : '$returnType $resultVar =';
229242
if (constBufferReturningFunctions.contains(field.name)) {
230243
returnCapture = 'const $returnCapture';
231244
}
232245
final callParams = [
233246
'jniEnv',
234-
...(outerFunctionType.parameters.map((param) => param.name).toList())
247+
...(outerFunctionType.parameters.map((param) => param.name).toList()),
248+
if (withVarArgs) 'args',
235249
].join(', ');
236250
final resultWrapper = getResultWrapper(returnType);
237251

238252
var convertRef = '';
239253
if (isJRefType(returnType) && !refFunctions.contains(field.name)) {
240254
convertRef = ' $resultVar = to_global_ref($resultVar);\n';
241255
}
256+
final callee = field.name + (withVarArgs ? 'V' : '');
257+
final varArgsInit = withVarArgs
258+
? '''
259+
va_list args;
260+
va_start(args, methodID);
261+
'''
262+
: '';
263+
final varArgsEnd = withVarArgs ? 'va_end(args);\n' : '';
242264
final exceptionCheck = _noCheckException.contains(field.name)
243265
? ''
244-
: ' jthrowable $errorVar = check_exception();\n'
245-
' if ($errorVar != NULL) {\n'
246-
' return ${resultWrapper.onError};\n'
247-
' }\n';
248-
return '${resultWrapper.returnType} $wrapperName($params) {\n'
266+
: '''
267+
jthrowable $errorVar = check_exception();
268+
if ($errorVar != NULL) {
269+
return ${resultWrapper.onError};
270+
}
271+
''';
272+
final willExport = withVarArgs ? 'FFI_PLUGIN_EXPORT ' : '';
273+
return '$willExport'
274+
'${resultWrapper.returnType} $wrapperName($params) {\n'
249275
' attach_thread();\n'
250-
' $returnCapture (*jniEnv)->${field.name}($callParams);\n'
276+
'$varArgsInit'
277+
' $returnCapture (*jniEnv)->$callee($callParams);\n'
278+
'$varArgsEnd'
251279
'$exceptionCheck'
252280
'$convertRef'
253281
' return ${resultWrapper.onResult};\n'
@@ -259,11 +287,20 @@ String? getWrapperFunc(Member field) {
259287
void writeGlobalJniEnvWrapper(Library library) {
260288
final jniEnvType = findCompound(library, envType);
261289

262-
final fieldDecls = jniEnvType.members.map(getFunctionFieldDecl).join('\n');
290+
final fieldDecls = jniEnvType.members
291+
.map((member) => getFunctionFieldDecl(member, isField: true))
292+
.join('\n');
293+
final varArgsFunctions = jniEnvType.members
294+
.where((member) => hasVarArgs(member.name))
295+
.map((member) => getFunctionFieldDecl(member, isField: false))
296+
.join('\n');
263297
final structDecl =
264298
'typedef struct $wrapperName {\n$fieldDecls\n} $wrapperName;\n';
265-
File.fromUri(Paths.globalJniEnvH).writeAsStringSync(
266-
'$preamble$wrapperDeclIncludes$structDecl$wrapperGetterDecl');
299+
File.fromUri(Paths.globalJniEnvH).writeAsStringSync('$preamble'
300+
'$wrapperDeclIncludes'
301+
'$structDecl'
302+
'$wrapperGetterDecl'
303+
'$varArgsFunctions\n');
267304

268305
final functionWrappers = StringBuffer();
269306
final structInst = StringBuffer('$wrapperName globalJniEnv = {\n');

0 commit comments

Comments
 (0)