@@ -53,7 +53,6 @@ class JsUtilWasmOptimizer extends Transformer {
5353 final Procedure _allowInteropTarget;
5454 final Procedure _numToInt;
5555 final Class _wasmExternRefClass;
56- final Class _objectClass;
5756 final Class _pragmaClass;
5857 final Field _pragmaName;
5958 final Field _pragmaOptions;
@@ -91,7 +90,6 @@ class JsUtilWasmOptimizer extends Transformer {
9190 .getClass ('dart:core' , 'num' )
9291 .procedures
9392 .firstWhere ((p) => p.name.text == 'toInt' ),
94- _objectClass = _coreTypes.objectClass,
9593 _pragmaClass = _coreTypes.pragmaClass,
9694 _pragmaName = _coreTypes.pragmaName,
9795 _pragmaOptions = _coreTypes.pragmaOptions,
@@ -232,88 +230,117 @@ class JsUtilWasmOptimizer extends Transformer {
232230 DartType get _nonNullableObjectType =>
233231 _coreTypes.objectRawType (Nullability .nonNullable);
234232
235- Expression ? _getInitializerFromTearOff (InstanceTearOff tearOff, int i) =>
236- tearOff.interfaceTargetReference.asProcedure.function
237- .positionalParameters[i].initializer;
233+ Expression _variableCheckConstant (
234+ VariableDeclaration variable, Constant constant) =>
235+ StaticInvocation (_coreTypes.identicalProcedure,
236+ Arguments ([VariableGet (variable), ConstantExpression (constant)]));
237+
238+ Expression _variableNullCheck (VariableDeclaration variable) =>
239+ _variableCheckConstant (variable, NullConstant ());
240+
241+ List <Expression > _generateCallbackArguments (
242+ FunctionType function, List <VariableDeclaration > positionalParameters,
243+ [int ? requiredParameterCount]) {
244+ List <Expression > callbackArguments = [];
245+ int length = requiredParameterCount ?? function.positionalParameters.length;
246+ for (int i = 0 ; i < length; i++ ) {
247+ callbackArguments.add (AsExpression (VariableGet (positionalParameters[i]),
248+ function.positionalParameters[i]));
249+ }
250+ return callbackArguments;
251+ }
252+
253+ Statement _generateDispatchCase (
254+ FunctionType function,
255+ VariableDeclaration callbackVariable,
256+ List <VariableDeclaration > positionalParameters,
257+ [int ? requiredParameterCount]) =>
258+ ReturnStatement (StaticInvocation (
259+ _jsifyRawTarget,
260+ Arguments ([
261+ FunctionInvocation (
262+ FunctionAccessKind .FunctionType ,
263+ AsExpression (VariableGet (callbackVariable), function),
264+ Arguments (_generateCallbackArguments (
265+ function, positionalParameters, requiredParameterCount)),
266+ functionType: function),
267+ ])));
268+
269+ /// Builds the body of a function trampoline. To support default arguments, we
270+ /// find the last defined argument in JS, that is the last argument which was
271+ /// explicitly passed by the user, and then we dispatch to a Dart function
272+ /// with the right number of arguments.
273+ Statement _createFunctionTrampolineBody (
274+ FunctionType function,
275+ VariableDeclaration callbackVariable,
276+ VariableDeclaration lastDefinedArgument,
277+ List <VariableDeclaration > positionalParameters) {
278+ // Handle cases where some or all arguments are undefined.
279+ // TODO(joshualitt): Consider using a switch instead.
280+ List <Statement > dispatchCases = [];
281+ for (int i = function.requiredParameterCount - 1 ;
282+ i < function.positionalParameters.length;
283+ i++ ) {
284+ // In this case, [i] is the last defined argument which can range from
285+ // -1(no arguments defined), to an actual index in the positional
286+ // parameters. [_generateDispatchCase] must also take the required
287+ // parameter count, which is always the index of the last defined argument
288+ // + 1, i.e. the total number of defined arguments.
289+ int requiredParameterCount = i + 1 ;
290+ dispatchCases.add (IfStatement (
291+ _variableCheckConstant (
292+ lastDefinedArgument, DoubleConstant (i.toDouble ())),
293+ _generateDispatchCase (function, callbackVariable,
294+ positionalParameters, requiredParameterCount),
295+ null ));
296+ }
297+
298+ // Finally handle the case where all arguments are defined.
299+ dispatchCases.add (_generateDispatchCase (
300+ function, callbackVariable, positionalParameters));
301+
302+ return Block (dispatchCases);
303+ }
238304
239305 /// Creates a callback trampoline for the given [function] . This callback
240- /// trampoline expects a Dart callback as its first argument, followed by all
241- /// of the arguments to the Dart callback as Dart objects. The trampoline will
242- /// cast all incoming Dart objects to the appropriate types, dispatch, and
243- /// then `jsifyRaw` any returned value. [_createFunctionTrampoline] Returns a
244- /// [String] function name representing the name of the wrapping function.
306+ /// trampoline expects a Dart callback as its first argument, then an integer
307+ /// value(double type) indicating the position of the last defined argument,
308+ /// followed by all of the arguments to the Dart callback as Dart objects. We
309+ /// will always pad the argument list up to the maximum number of positional
310+ /// arguments with `undefined` values. The trampoline will cast all incoming
311+ /// Dart objects to the appropriate types, dispatch, and then `jsifyRaw` any
312+ /// returned value. [_createFunctionTrampoline] Returns a [String] function
313+ /// name representing the name of the wrapping function.
245314 /// TODO(joshualitt): Share callback trampolines if the [FunctionType]
246315 /// matches.
247- String _createFunctionTrampoline (
248- Procedure node, FunctionType function, Expression argument) {
316+ /// TODO(joshualitt): Simplify the trampoline in JS for the case where there
317+ /// are no default arguments.
318+ String _createFunctionTrampoline (Procedure node, FunctionType function) {
249319 int fileOffset = node.fileOffset;
250320
251321 // Create arguments for each positional parameter in the function. These
252322 // arguments will be converted in JS to Dart objects. The generated wrapper
253323 // will cast each argument to the correct type. The first argument to this
254324 // function will be the Dart callback, which will be cast to the supplied
255- // [FunctionType] before being invoked.
325+ // [FunctionType] before being invoked. The second argument will be the
326+ // last defined argument which is necessary to support default arguments in
327+ // callbacks.
256328 int parameterId = 1 ;
257- DartType nonNullableObjectType =
258- _objectClass.getThisType (_coreTypes, Nullability .nonNullable);
259329 final callbackVariable =
260- VariableDeclaration ('callback' , type: nonNullableObjectType);
261- List <VariableDeclaration > positionalParameters = [callbackVariable];
262- List <Expression > callbackArguments = [];
263- DartType nullableObjectType =
264- _objectClass.getThisType (_coreTypes, Nullability .nullable);
265- for (int i = 0 ; i < function.positionalParameters.length; i++ ) {
266- DartType type = function.positionalParameters[i];
267- Expression ? defaultExpression;
268- bool hasDefault = i >= function.requiredParameterCount;
269- if (hasDefault) {
270- // We can only generate default values if we have a statically typed
271- // function argument.
272- Expression ? initializer;
273- if (argument is ConstantExpression ) {
274- Procedure callbackTarget = (argument.constant as TearOffConstant )
275- .targetReference
276- .asProcedure;
277- initializer =
278- callbackTarget.function.positionalParameters[i].initializer;
279- } else if (argument is FunctionExpression ) {
280- initializer = argument.function.positionalParameters[i].initializer;
281- } else if (argument is InstanceTearOff ) {
282- initializer = _getInitializerFromTearOff (argument, i);
283- } else if (argument is AsExpression &&
284- argument.operand is InstanceTearOff ) {
285- initializer = _getInitializerFromTearOff (
286- argument.operand as InstanceTearOff , i);
287- } else {
288- throw 'Cannot pass default arguments.' ;
289- }
290-
291- // The initializer for a default argument must be a
292- // [ConstantExpression].
293- ConstantExpression init = initializer as ConstantExpression ;
294- defaultExpression = ConstantExpression (init.constant, init.type);
295- }
296- VariableDeclaration variable =
297- VariableDeclaration ('x${parameterId ++}' , type: nullableObjectType);
298- positionalParameters.add (variable);
299- Expression body;
300- if (hasDefault) {
301- body = ConditionalExpression (
302- StaticInvocation (
303- _coreTypes.identicalProcedure,
304- Arguments ([
305- VariableGet (variable),
306- ConstantExpression (NullConstant ())
307- ])),
308- defaultExpression ?? ConstantExpression (NullConstant ()),
309- VariableGet (variable),
310- nullableObjectType);
311- } else {
312- body = VariableGet (variable);
313- }
314- callbackArguments.add (AsExpression (body, type));
330+ VariableDeclaration ('callback' , type: _nonNullableObjectType);
331+ final lastDefinedArgument = VariableDeclaration ('lastDefinedArgument' ,
332+ type: _coreTypes.doubleNonNullableRawType);
333+
334+ // Initialize variable declarations.
335+ List <VariableDeclaration > positionalParameters = [];
336+ for (int j = 0 ; j < function.positionalParameters.length; j++ ) {
337+ positionalParameters.add (
338+ VariableDeclaration ('x${parameterId ++}' , type: _nullableObjectType));
315339 }
316340
341+ Statement functionTrampolineBody = _createFunctionTrampolineBody (
342+ function, callbackVariable, lastDefinedArgument, positionalParameters);
343+
317344 // Create a new procedure for the callback trampoline. This procedure will
318345 // be exported from Wasm to JS so it can be called from JS. The argument
319346 // returned from the supplied callback will be converted with `jsifyRaw` to
@@ -327,17 +354,10 @@ class JsUtilWasmOptimizer extends Transformer {
327354 final functionTrampoline = Procedure (
328355 Name (functionTrampolineName, _library),
329356 ProcedureKind .Method ,
330- FunctionNode (
331- ReturnStatement (StaticInvocation (
332- _jsifyRawTarget,
333- Arguments ([
334- FunctionInvocation (
335- FunctionAccessKind .FunctionType ,
336- AsExpression (VariableGet (callbackVariable), function),
337- Arguments (callbackArguments),
338- functionType: function),
339- ]))),
340- positionalParameters: positionalParameters,
357+ FunctionNode (functionTrampolineBody,
358+ positionalParameters: [callbackVariable, lastDefinedArgument]
359+ .followedBy (positionalParameters)
360+ .toList (),
341361 returnType: nullableWasmExternRefType)
342362 ..fileOffset = fileOffset,
343363 isStatic: true ,
@@ -358,8 +378,7 @@ class JsUtilWasmOptimizer extends Transformer {
358378 /// [_createFunctionTrampoline] followed by `_wrapDartFunction` .
359379 StaticInvocation _allowInterop (
360380 Procedure node, FunctionType type, Expression argument) {
361- String functionTrampolineName =
362- _createFunctionTrampoline (node, type, argument);
381+ String functionTrampolineName = _createFunctionTrampoline (node, type);
363382 return StaticInvocation (
364383 _wrapDartFunctionTarget,
365384 Arguments ([
@@ -440,10 +459,7 @@ class JsUtilWasmOptimizer extends Transformer {
440459 return Let (
441460 v,
442461 ConditionalExpression (
443- StaticInvocation (
444- _coreTypes.identicalProcedure,
445- Arguments (
446- [VariableGet (v), ConstantExpression (NullConstant ())])),
462+ _variableNullCheck (v),
447463 ConstantExpression (NullConstant ()),
448464 InstanceInvocation (InstanceAccessKind .Instance , VariableGet (v),
449465 _numToInt.name, Arguments ([]),
0 commit comments