@@ -48,6 +48,15 @@ import 'type_environment.dart';
4848import 'type_recipe_generator.dart' ;
4949import 'type_table.dart' ;
5050
51+ /// The groups of compiled invocation arguments ready to be flattened into a
52+ /// single [js_ast.Array] for statically sound calls or into separate packets
53+ /// for runtime checked calls.
54+ typedef _ArgumentGroups = ({
55+ List <js_ast.Expression >? typeArguments,
56+ List <js_ast.Expression >? positionalArguments,
57+ List <js_ast.Property >? namedArguments
58+ });
59+
5160/// Public API for DDC compilers.
5261abstract class Compiler {
5362 js_ast.Program emitModule (Component component);
@@ -5263,22 +5272,23 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
52635272
52645273 @override
52655274 js_ast.Expression visitDynamicInvocation (DynamicInvocation node) {
5266- return _emitMethodCall (node.receiver, null , node.arguments, node);
5275+ return _emitDynamicInvocation (
5276+ node.receiver, node.name.text, node.arguments);
52675277 }
52685278
52695279 @override
52705280 js_ast.Expression visitFunctionInvocation (FunctionInvocation node) {
5271- assert (node.name.text == 'call' );
5272- var function = _visitExpression (node.receiver);
5273- var arguments = _emitArgumentList (node.arguments);
5281+ var name = node.name.text;
5282+ assert (name == 'call' );
52745283 if (node.functionType == null ) {
52755284 // A `null` here implies the receiver is typed as `Function`. There isn't
52765285 // any more type information available at compile time to know this
52775286 // invocation is sound so a dynamic call will handle the checks at
52785287 // runtime.
5279- return _emitDynamicInvoke (function, null , arguments , node.arguments);
5288+ return _emitDynamicInvocation (node.receiver, name , node.arguments);
52805289 }
5281- return js_ast.Call (function, arguments);
5290+ return js_ast.Call (
5291+ _visitExpression (node.receiver), _emitArgumentList (node.arguments));
52825292 }
52835293
52845294 @override
@@ -5320,7 +5330,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
53205330 negated: false );
53215331 }
53225332
5323- js_ast.Expression _emitMethodCall (Expression receiver, Member ? target,
5333+ js_ast.Expression _emitMethodCall (Expression receiver, Member target,
53245334 Arguments arguments, InvocationExpression node) {
53255335 var name = node.name.text;
53265336
@@ -5380,22 +5390,22 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
53805390 return js.call ('#.push(#)' , [jsReceiver, args]);
53815391 }
53825392
5383- var isCallingDynamicField = target is Member &&
5384- target.hasGetter &&
5393+ var isCallingDynamicField = target.hasGetter &&
53855394 // Erasing extension types here doesn't make sense. If there is an
53865395 // extension type on dynamic or Function it will only be callable if it
53875396 // defines a call method which would be invoked statically.
53885397 _isDynamicOrFunction (target.getterType);
53895398 if (name == 'call' ) {
5390- // Erasing the extension types here to support existing callable behaivor
5399+ // Erasing the extension types here to support existing callable behavior
53915400 // on the old style JS interop types that are callable. This should be
53925401 // safe as it is a compile time error to try to dynamically invoke a call
53935402 // method that is inherited from an extension type.
53945403 var receiverType =
53955404 receiver.getStaticType (_staticTypeContext).extensionTypeErasure;
5396- if (isCallingDynamicField || _isDynamicOrFunction (receiverType)) {
5397- return _emitDynamicInvoke (jsReceiver, null , args, arguments);
5398- } else if (_isDirectCallable (receiverType)) {
5405+ if (isCallingDynamicField) {
5406+ return _emitDynamicInvocation (receiver, name, arguments);
5407+ }
5408+ if (_isDirectCallable (receiverType)) {
53995409 // Call methods on function types should be handled as function calls.
54005410 return js_ast.Call (jsReceiver, args);
54015411 }
@@ -5412,8 +5422,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
54125422 return _runtimeCall ('#(#, #)' , [name, jsReceiver, args]);
54135423 }
54145424 // Otherwise generate this as a normal typed method call.
5415- } else if (target == null || isCallingDynamicField) {
5416- return _emitDynamicInvoke (jsReceiver, jsName, args , arguments);
5425+ } else if (isCallingDynamicField) {
5426+ return _emitDynamicInvocation (receiver, name , arguments);
54175427 }
54185428 // TODO(jmesserly): remove when Kernel desugars this for us.
54195429 // Handle `o.m(a)` where `o.m` is a getter returning a class with `call`.
@@ -5428,12 +5438,12 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
54285438 // }
54295439 // ```
54305440 //
5431- // We can always erase eagerly becuase :
5432- // - Extension types that do not implment an interface that exposes a
5441+ // We can always erase eagerly because :
5442+ // - Extension types that do not implement an interface that exposes a
54335443 // `call` method will result in a static error at the call site.
54345444 // - Calls to extension types that implement their own call method are
54355445 // lowered by the CFE to top level static method calls.
5436- var fromType = target! .getterType.extensionTypeErasure;
5446+ var fromType = target.getterType.extensionTypeErasure;
54375447 if (fromType is InterfaceType ) {
54385448 var callName = _implicitCallTarget (fromType);
54395449 if (callName != null ) {
@@ -5444,41 +5454,42 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
54445454 return js.call ('#.#(#)' , [jsReceiver, jsName, args]);
54455455 }
54465456
5447- js_ast. Expression _emitDynamicInvoke (
5448- js_ast. Expression fn,
5449- js_ast. Expression ? methodName,
5450- Iterable < js_ast.Expression > args,
5451- Arguments arguments) {
5452- var jsArgs = < Object > [fn ];
5457+ /// Returns an invocation of the runtime helpers `dcall` , `dgcall` , `dsend` ,
5458+ /// or `dgsend` to perform dynamic checks before invoking [memberName] on
5459+ /// [receiver] and passing [arguments] .
5460+ js_ast.Expression _emitDynamicInvocation (
5461+ Expression receiver, String memberName, Arguments arguments) {
5462+ var jsArgs = [ _visitExpression (receiver) ];
54535463 String jsCode;
5454-
5455- var typeArgs = arguments.types;
5456- if (typeArgs.isNotEmpty) {
5457- jsArgs.add (args.take (typeArgs.length));
5458- args = args.skip (typeArgs.length);
5459- if (methodName != null ) {
5460- jsCode = 'dgsend$_replSuffix (#, [#], #' ;
5461- jsArgs.add (methodName);
5464+ var (: typeArguments, : positionalArguments, : namedArguments) =
5465+ _emitArgumentGroups (arguments, isJSInterop: false );
5466+ if (memberName == 'call' ) {
5467+ if (typeArguments != null ) {
5468+ jsCode = 'dgcall(#, #' ;
5469+ jsArgs.add (js_ast.ArrayInitializer (typeArguments));
54625470 } else {
5463- jsCode = 'dgcall(#, [#] ' ;
5471+ jsCode = 'dcall(# ' ;
54645472 }
5465- } else if (methodName != null ) {
5466- jsCode = 'dsend$_replSuffix (#, #' ;
5467- jsArgs.add (methodName);
54685473 } else {
5469- jsCode = 'dcall(#' ;
5474+ if (typeArguments != null ) {
5475+ jsCode = 'dgsend$_replSuffix (#, #, #' ;
5476+ jsArgs.add (js_ast.ArrayInitializer (typeArguments));
5477+ } else {
5478+ jsCode = 'dsend$_replSuffix (#, #' ;
5479+ }
5480+ jsArgs.add (_emitMemberName (memberName));
54705481 }
5471-
5472- var hasNamed = arguments.named.isNotEmpty;
5473- if (hasNamed) {
5474- jsCode += ', [#], #)' ;
5475- jsArgs.add (args.take (args.length - 1 ));
5476- jsArgs.add (args.last);
5482+ if (positionalArguments != null ) {
5483+ jsCode += ', #' ;
5484+ jsArgs.add (js_ast.ArrayInitializer (positionalArguments));
54775485 } else {
5478- jsArgs.add (args);
5479- jsCode += ', [#])' ;
5486+ jsCode += ', []' ;
54805487 }
5481-
5488+ if (namedArguments != null ) {
5489+ jsCode += ', #' ;
5490+ jsArgs.add (js_ast.ObjectInitializer (namedArguments));
5491+ }
5492+ jsCode += ')' ;
54825493 return _runtimeCall (jsCode, jsArgs);
54835494 }
54845495
@@ -6384,29 +6395,65 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
63846395 return _emitTopLevelName (target);
63856396 }
63866397
6398+ /// Returns all parts of [arguments] flattened into a list so they can be
6399+ /// passed in the calling convention for calls with no runtime checks.
6400+ ///
6401+ /// When [types] is `false` any type arguments present in [arguments] will be
6402+ /// omitted.
6403+ ///
6404+ /// Passing [target] when applicable allows for detection of an annotation to
6405+ /// omit passing type parameters and to detect if positional arguments should
6406+ /// be packaged to be passed to a JavaScript using an interop call.
63876407 List <js_ast.Expression > _emitArgumentList (Arguments node,
63886408 {bool types = true , Member ? target}) {
63896409 types = types && _reifyGenericFunction (target);
6390- final isJsInterop = target != null && isJsMember (target);
6410+ var (: typeArguments, : positionalArguments, : namedArguments) =
6411+ _emitArgumentGroups (node,
6412+ isJSInterop: target != null && isJsMember (target));
63916413 return [
6392- if (types)
6393- for (var typeArg in node.types) _emitType (typeArg),
6394- for (var arg in node.positional)
6395- if (arg is StaticInvocation &&
6396- isJSSpreadInvocation (arg.target) &&
6397- arg.arguments.positional.length == 1 )
6398- js_ast.Spread (_visitExpression (arg.arguments.positional[0 ]))
6399- else if (isJsInterop)
6400- _visitExpression (_assertInterop (arg))
6401- else
6402- _visitExpression (arg),
6403- if (node.named.isNotEmpty)
6404- js_ast.ObjectInitializer ([
6405- for (var arg in node.named) _emitNamedExpression (arg, isJsInterop)
6406- ]),
6414+ if (types && typeArguments != null ) ...typeArguments,
6415+ if (positionalArguments != null ) ...positionalArguments,
6416+ if (namedArguments != null ) js_ast.ObjectInitializer ([...namedArguments]),
64076417 ];
64086418 }
64096419
6420+ /// Returns all [arguments] but kept in separate packets so they can be
6421+ /// further processed.
6422+ ///
6423+ /// Facilitates passing arguments in the calling convention used by runtime
6424+ /// helpers that check arguments.
6425+ ///
6426+ /// When [isJSInterop] is `true` the positional arguments are packaged to
6427+ /// be passed to JavaScript via an interop call.
6428+ _ArgumentGroups _emitArgumentGroups (Arguments node,
6429+ {required bool isJSInterop}) {
6430+ var typeArguments = node.types.isEmpty
6431+ ? null
6432+ : [for (var typeArgument in node.types) _emitType (typeArgument)];
6433+ var positionalArguments = node.positional.isEmpty
6434+ ? null
6435+ : [
6436+ for (var arg in node.positional)
6437+ if (arg is StaticInvocation &&
6438+ isJSSpreadInvocation (arg.target) &&
6439+ arg.arguments.positional.length == 1 )
6440+ js_ast.Spread (_visitExpression (arg.arguments.positional[0 ]))
6441+ else if (isJSInterop)
6442+ _visitExpression (_assertInterop (arg))
6443+ else
6444+ _visitExpression (arg),
6445+ ];
6446+ var namedArguments = node.named.isEmpty
6447+ ? null
6448+ : [for (var arg in node.named) _emitNamedExpression (arg, isJSInterop)];
6449+
6450+ return (
6451+ typeArguments: typeArguments,
6452+ positionalArguments: positionalArguments,
6453+ namedArguments: namedArguments
6454+ );
6455+ }
6456+
64106457 js_ast.Property _emitNamedExpression (NamedExpression arg,
64116458 [bool isJsInterop = false ]) {
64126459 var value = isJsInterop ? _assertInterop (arg.value) : arg.value;
0 commit comments