Skip to content

Commit 776c058

Browse files
nshahanCommit Queue
authored andcommitted
[ddc] Refactor visitInstanceGetterInvocation()
Moves the remaining special case logic out of `_emitMethodCall()` simplifying the reasoning about where so hot reload soundness checks can be added. Change-Id: I13e2f451e61f6e067ea689bcc35039cf92946492 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/423603 Reviewed-by: Mark Zhou <[email protected]> Reviewed-by: Nate Biggs <[email protected]> Commit-Queue: Nicholas Shahan <[email protected]>
1 parent bde40ce commit 776c058

File tree

2 files changed

+142
-137
lines changed

2 files changed

+142
-137
lines changed

pkg/dev_compiler/lib/src/kernel/compiler.dart

Lines changed: 71 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5293,8 +5293,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
52935293

52945294
@override
52955295
js_ast.Expression visitInstanceInvocation(InstanceInvocation node) {
5296-
var invocation = _emitMethodCall(
5297-
node.receiver, node.interfaceTarget, node.arguments, node);
5296+
var invocation = _emitInstanceInvocation(node);
52985297
return _isNullCheckableJsInterop(node.interfaceTarget)
52995298
? _wrapWithJsInteropNullCheck(invocation)
53005299
: invocation;
@@ -5303,13 +5302,64 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
53035302
@override
53045303
js_ast.Expression visitInstanceGetterInvocation(
53055304
InstanceGetterInvocation node) {
5306-
var getterInvocation = _emitMethodCall(
5307-
node.receiver, node.interfaceTarget, node.arguments, 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);
53085314
return _isNullCheckableJsInterop(node.interfaceTarget)
53095315
? _wrapWithJsInteropNullCheck(getterInvocation)
53105316
: getterInvocation;
53115317
}
53125318

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+
53135363
@override
53145364
js_ast.Expression visitLocalFunctionInvocation(LocalFunctionInvocation node) {
53155365
assert(node.name.text == 'call');
@@ -5330,20 +5380,15 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
53305380
negated: false);
53315381
}
53325382

5333-
js_ast.Expression _emitMethodCall(Expression receiver, Member target,
5334-
Arguments arguments, InvocationExpression node) {
5335-
var name = node.name.text;
5336-
5383+
js_ast.Expression _emitInstanceInvocation(InstanceInvocation node) {
53375384
/// Returns `true` when [node] represents an invocation of `List.add()` that
53385385
/// can be optimized.
53395386
///
53405387
/// The optimized add operation can skip checks for a growable or modifiable
53415388
/// list and the element type is known to be invariant so it can skip the
53425389
/// type check.
5343-
bool isNativeListInvariantAdd(InvocationExpression node) {
5344-
if (node is InstanceInvocation &&
5345-
node.isInvariant &&
5346-
node.name.text == 'add') {
5390+
bool isNativeListInvariantAdd(InstanceInvocation node) {
5391+
if (node.isInvariant && node.name.text == 'add') {
53475392
// The call to add is marked as invariant, so the type check on the
53485393
// parameter to add is not needed.
53495394
var receiver = node.receiver;
@@ -5373,6 +5418,10 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
53735418
return false;
53745419
}
53755420

5421+
var name = node.name.text;
5422+
var receiver = node.receiver;
5423+
var arguments = node.arguments;
5424+
var target = node.interfaceTarget;
53765425
if (isOperatorMethodName(name) && arguments.named.isEmpty) {
53775426
var argLength = arguments.positional.length;
53785427
if (argLength == 0) {
@@ -5382,75 +5431,33 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
53825431
receiver, target, arguments.positional[0], node);
53835432
}
53845433
}
5385-
53865434
var jsReceiver = _visitExpression(receiver);
53875435
var args = _emitArgumentList(arguments, target: target);
5388-
53895436
if (isNativeListInvariantAdd(node)) {
53905437
return js.call('#.push(#)', [jsReceiver, args]);
53915438
}
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);
53985439
if (name == 'call') {
53995440
// Erasing the extension types here to support existing callable behavior
54005441
// on the old style JS interop types that are callable. This should be
54015442
// safe as it is a compile time error to try to dynamically invoke a call
54025443
// method that is inherited from an extension type.
54035444
var receiverType =
54045445
receiver.getStaticType(_staticTypeContext).extensionTypeErasure;
5405-
if (isCallingDynamicField) {
5406-
return _emitDynamicInvocation(receiver, name, arguments);
5407-
}
54085446
if (_isDirectCallable(receiverType)) {
5409-
// Call methods on function types should be handled as function calls.
5447+
// Handle call methods on function types as function calls.
54105448
return js_ast.Call(jsReceiver, args);
54115449
}
54125450
}
5413-
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-
}
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]);
54535458
}
5459+
// Otherwise generate this as a normal typed method call.
5460+
var jsName = _emitMemberName(name, member: target);
54545461
return js.call('#.#(#)', [jsReceiver, jsName, args]);
54555462
}
54565463

@@ -5505,11 +5512,6 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
55055512
return null;
55065513
}
55075514

5508-
bool _isDynamicOrFunction(DartType t) =>
5509-
DartTypeEquivalence(_coreTypes, ignoreTopLevelNullability: true)
5510-
.areEqual(t, _coreTypes.functionNonNullableRawType) ||
5511-
t == const DynamicType();
5512-
55135515
js_ast.Expression _emitUnaryOperator(
55145516
Expression expr, Member? target, InvocationExpression node) {
55155517
var op = node.name.text;

0 commit comments

Comments
 (0)