@@ -5293,7 +5293,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
5293
5293
5294
5294
@override
5295
5295
js_ast.Expression visitInstanceInvocation (InstanceInvocation node) {
5296
- var invocation = _emitInstanceInvocation (node);
5296
+ var invocation = _emitMethodCall (
5297
+ node.receiver, node.interfaceTarget, node.arguments, node);
5297
5298
return _isNullCheckableJsInterop (node.interfaceTarget)
5298
5299
? _wrapWithJsInteropNullCheck (invocation)
5299
5300
: invocation;
@@ -5302,64 +5303,13 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
5302
5303
@override
5303
5304
js_ast.Expression visitInstanceGetterInvocation (
5304
5305
InstanceGetterInvocation node) {
5305
- if (node.functionType == null ) {
5306
- // A `null` here implies the receiver must be typed as `dynamic` or
5307
- // `Function`. There isn't any more type information available at compile
5308
- // time to know this invocation is sound so a dynamic call will handle the
5309
- // checks at runtime.
5310
- return _emitDynamicInvocation (
5311
- node.receiver, node.name.text, node.arguments);
5312
- }
5313
- var getterInvocation = _emitInstanceGetterInvocation (node);
5306
+ var getterInvocation = _emitMethodCall (
5307
+ node.receiver, node.interfaceTarget, node.arguments, node);
5314
5308
return _isNullCheckableJsInterop (node.interfaceTarget)
5315
5309
? _wrapWithJsInteropNullCheck (getterInvocation)
5316
5310
: getterInvocation;
5317
5311
}
5318
5312
5319
- js_ast.Expression _emitInstanceGetterInvocation (
5320
- InstanceGetterInvocation node) {
5321
- var receiver = _visitExpression (node.receiver);
5322
- var arguments =
5323
- _emitArgumentList (node.arguments, target: node.interfaceTarget);
5324
- if (node.name.text == 'call' ) {
5325
- // Erasing the extension types here to support existing callable behavior
5326
- // on the old style JS interop types that are callable. This should be
5327
- // safe as it is a compile time error to try to dynamically invoke a call
5328
- // method that is inherited from an extension type.
5329
- var receiverType =
5330
- node.receiver.getStaticType (_staticTypeContext).extensionTypeErasure;
5331
- if (_isDirectCallable (receiverType)) {
5332
- // Call methods on function types should be handled as function calls.
5333
- return js_ast.Call (receiver, arguments);
5334
- }
5335
- }
5336
- var memberName =
5337
- _emitMemberName (node.name.text, member: node.interfaceTarget);
5338
- // We must erase the extension type to potentially find the `call` method.
5339
- // If the extension type has a runtime representation with a `call`:
5340
- //
5341
- // ```
5342
- // extension type Ext(C c) implements C {...}
5343
- // class C {
5344
- // call() {...}
5345
- // }
5346
- // ```
5347
- //
5348
- // We can always erase eagerly because:
5349
- // - Extension types that do not implement an interface that exposes a
5350
- // `call` method will result in a static error at the call site.
5351
- // - Calls to extension types that implement their own call method are
5352
- // lowered by the CFE to top level static method calls.
5353
- var erasedGetterType = node.interfaceTarget.getterType.extensionTypeErasure;
5354
- if (erasedGetterType is InterfaceType ) {
5355
- var callName = _implicitCallTarget (erasedGetterType);
5356
- if (callName != null ) {
5357
- return js.call ('#.#.#(#)' , [receiver, memberName, callName, arguments]);
5358
- }
5359
- }
5360
- return js.call ('#.#(#)' , [receiver, memberName, arguments]);
5361
- }
5362
-
5363
5313
@override
5364
5314
js_ast.Expression visitLocalFunctionInvocation (LocalFunctionInvocation node) {
5365
5315
assert (node.name.text == 'call' );
@@ -5380,15 +5330,20 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
5380
5330
negated: false );
5381
5331
}
5382
5332
5383
- js_ast.Expression _emitInstanceInvocation (InstanceInvocation node) {
5333
+ js_ast.Expression _emitMethodCall (Expression receiver, Member target,
5334
+ Arguments arguments, InvocationExpression node) {
5335
+ var name = node.name.text;
5336
+
5384
5337
/// Returns `true` when [node] represents an invocation of `List.add()` that
5385
5338
/// can be optimized.
5386
5339
///
5387
5340
/// The optimized add operation can skip checks for a growable or modifiable
5388
5341
/// list and the element type is known to be invariant so it can skip the
5389
5342
/// type check.
5390
- bool isNativeListInvariantAdd (InstanceInvocation node) {
5391
- if (node.isInvariant && node.name.text == 'add' ) {
5343
+ bool isNativeListInvariantAdd (InvocationExpression node) {
5344
+ if (node is InstanceInvocation &&
5345
+ node.isInvariant &&
5346
+ node.name.text == 'add' ) {
5392
5347
// The call to add is marked as invariant, so the type check on the
5393
5348
// parameter to add is not needed.
5394
5349
var receiver = node.receiver;
@@ -5418,10 +5373,6 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
5418
5373
return false ;
5419
5374
}
5420
5375
5421
- var name = node.name.text;
5422
- var receiver = node.receiver;
5423
- var arguments = node.arguments;
5424
- var target = node.interfaceTarget;
5425
5376
if (isOperatorMethodName (name) && arguments.named.isEmpty) {
5426
5377
var argLength = arguments.positional.length;
5427
5378
if (argLength == 0 ) {
@@ -5431,33 +5382,75 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
5431
5382
receiver, target, arguments.positional[0 ], node);
5432
5383
}
5433
5384
}
5385
+
5434
5386
var jsReceiver = _visitExpression (receiver);
5435
5387
var args = _emitArgumentList (arguments, target: target);
5388
+
5436
5389
if (isNativeListInvariantAdd (node)) {
5437
5390
return js.call ('#.push(#)' , [jsReceiver, args]);
5438
5391
}
5392
+
5393
+ var isCallingDynamicField = target.hasGetter &&
5394
+ // Erasing extension types here doesn't make sense. If there is an
5395
+ // extension type on dynamic or Function it will only be callable if it
5396
+ // defines a call method which would be invoked statically.
5397
+ _isDynamicOrFunction (target.getterType);
5439
5398
if (name == 'call' ) {
5440
5399
// Erasing the extension types here to support existing callable behavior
5441
5400
// on the old style JS interop types that are callable. This should be
5442
5401
// safe as it is a compile time error to try to dynamically invoke a call
5443
5402
// method that is inherited from an extension type.
5444
5403
var receiverType =
5445
5404
receiver.getStaticType (_staticTypeContext).extensionTypeErasure;
5405
+ if (isCallingDynamicField) {
5406
+ return _emitDynamicInvocation (receiver, name, arguments);
5407
+ }
5446
5408
if (_isDirectCallable (receiverType)) {
5447
- // Handle call methods on function types as function calls.
5409
+ // Call methods on function types should be handled as function calls.
5448
5410
return js_ast.Call (jsReceiver, args);
5449
5411
}
5450
5412
}
5451
- if (_isObjectMethodCall (name, arguments) &&
5452
- _shouldCallObjectMemberHelper (receiver)) {
5453
- // Handle Object methods that are supported by `null` and possibly
5454
- // JavaScript interop values with static helper methods.
5455
- // The names of the static helper methods in the runtime must match the
5456
- // names of the Object instance members.
5457
- return _runtimeCall ('#(#, #)' , [name, jsReceiver, args]);
5458
- }
5459
- // Otherwise generate this as a normal typed method call.
5413
+
5460
5414
var jsName = _emitMemberName (name, member: target);
5415
+
5416
+ // Handle Object methods that are supported by `null` and potentially
5417
+ // JavaScript interop values.
5418
+ if (_isObjectMethodCall (name, arguments)) {
5419
+ if (_shouldCallObjectMemberHelper (receiver)) {
5420
+ // The names of the static helper methods in the runtime must match the
5421
+ // names of the Object instance members.
5422
+ return _runtimeCall ('#(#, #)' , [name, jsReceiver, args]);
5423
+ }
5424
+ // Otherwise generate this as a normal typed method call.
5425
+ } else if (isCallingDynamicField) {
5426
+ return _emitDynamicInvocation (receiver, name, arguments);
5427
+ }
5428
+ // TODO(jmesserly): remove when Kernel desugars this for us.
5429
+ // Handle `o.m(a)` where `o.m` is a getter returning a class with `call`.
5430
+ if (target is Field || target is Procedure && target.isAccessor) {
5431
+ // We must erase the extension type to find the `call` method.
5432
+ // If the extension type has a runtime representation with a `call`:
5433
+ //
5434
+ // ```
5435
+ // extension type Ext(C c) implements C {...}
5436
+ // class C {
5437
+ // call() {...}
5438
+ // }
5439
+ // ```
5440
+ //
5441
+ // We can always erase eagerly because:
5442
+ // - Extension types that do not implement an interface that exposes a
5443
+ // `call` method will result in a static error at the call site.
5444
+ // - Calls to extension types that implement their own call method are
5445
+ // lowered by the CFE to top level static method calls.
5446
+ var fromType = target.getterType.extensionTypeErasure;
5447
+ if (fromType is InterfaceType ) {
5448
+ var callName = _implicitCallTarget (fromType);
5449
+ if (callName != null ) {
5450
+ return js.call ('#.#.#(#)' , [jsReceiver, jsName, callName, args]);
5451
+ }
5452
+ }
5453
+ }
5461
5454
return js.call ('#.#(#)' , [jsReceiver, jsName, args]);
5462
5455
}
5463
5456
@@ -5512,6 +5505,11 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
5512
5505
return null ;
5513
5506
}
5514
5507
5508
+ bool _isDynamicOrFunction (DartType t) =>
5509
+ DartTypeEquivalence (_coreTypes, ignoreTopLevelNullability: true )
5510
+ .areEqual (t, _coreTypes.functionNonNullableRawType) ||
5511
+ t == const DynamicType ();
5512
+
5515
5513
js_ast.Expression _emitUnaryOperator (
5516
5514
Expression expr, Member ? target, InvocationExpression node) {
5517
5515
var op = node.name.text;
0 commit comments