@@ -56,6 +56,11 @@ const wrapperGetterDecl = '''
56
56
FFI_PLUGIN_EXPORT $wrapperName * GetGlobalEnv();
57
57
''' ;
58
58
59
+ bool hasVarArgs (String name) {
60
+ return name == 'NewObject' ||
61
+ RegExp (r'^Call(Static|Nonvirtual|)[A-Z][a-z]+Method$' ).hasMatch (name);
62
+ }
63
+
59
64
/// Get C name of a type from its ffigen representation.
60
65
String getCType (Type type) {
61
66
if (type is PointerType ) {
@@ -81,19 +86,24 @@ FunctionType getGlobalJniEnvFunctionType(FunctionType ft) {
81
86
}
82
87
83
88
// Returns declaration of function field in GlobalJniEnv struct
84
- String getFunctionFieldDecl (
85
- Member field,
86
- ) {
89
+ String getFunctionFieldDecl (Member field, {required bool isField}) {
87
90
final fieldType = field.type;
88
91
if (fieldType is PointerType && fieldType.child is NativeFunc ) {
89
92
final nativeFunc = fieldType.child as NativeFunc ;
90
93
final functionType = getGlobalJniEnvFunctionType (nativeFunc.type);
91
94
final resultWrapper = getResultWrapper (getCType (functionType.returnType));
92
95
final name = field.name;
96
+ final withVarArgs = hasVarArgs (name);
93
97
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 );' ;
97
107
} else {
98
108
return 'void* ${field .name };' ;
99
109
}
@@ -222,32 +232,50 @@ String? getWrapperFunc(Member field) {
222
232
final outerFunctionType = getGlobalJniEnvFunctionType (functionType);
223
233
final wrapperName = getWrapperFuncName (field);
224
234
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 (', ' );
228
241
var returnCapture = returnType == 'void' ? '' : '$returnType $resultVar =' ;
229
242
if (constBufferReturningFunctions.contains (field.name)) {
230
243
returnCapture = 'const $returnCapture ' ;
231
244
}
232
245
final callParams = [
233
246
'jniEnv' ,
234
- ...(outerFunctionType.parameters.map ((param) => param.name).toList ())
247
+ ...(outerFunctionType.parameters.map ((param) => param.name).toList ()),
248
+ if (withVarArgs) 'args' ,
235
249
].join (', ' );
236
250
final resultWrapper = getResultWrapper (returnType);
237
251
238
252
var convertRef = '' ;
239
253
if (isJRefType (returnType) && ! refFunctions.contains (field.name)) {
240
254
convertRef = ' $resultVar = to_global_ref($resultVar );\n ' ;
241
255
}
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 ' : '' ;
242
264
final exceptionCheck = _noCheckException.contains (field.name)
243
265
? ''
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 '
249
275
' attach_thread();\n '
250
- ' $returnCapture (*jniEnv)->${field .name }($callParams );\n '
276
+ '$varArgsInit '
277
+ ' $returnCapture (*jniEnv)->$callee ($callParams );\n '
278
+ '$varArgsEnd '
251
279
'$exceptionCheck '
252
280
'$convertRef '
253
281
' return ${resultWrapper .onResult };\n '
@@ -259,11 +287,20 @@ String? getWrapperFunc(Member field) {
259
287
void writeGlobalJniEnvWrapper (Library library) {
260
288
final jniEnvType = findCompound (library, envType);
261
289
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 ' );
263
297
final structDecl =
264
298
'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 ' );
267
304
268
305
final functionWrappers = StringBuffer ();
269
306
final structInst = StringBuffer ('$wrapperName globalJniEnv = {\n ' );
0 commit comments