Skip to content

Commit 41ff6c3

Browse files
nshahanCommit Queue
authored andcommitted
[ddc] Refactor visitInstanceInvocation
This is in preparation to handle extra hot reload checks various instance method invocations. Change-Id: I9aaa9a29ec76c44466ce4b384a42ca980e7d8ca6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/440081 Reviewed-by: Nate Biggs <[email protected]> Reviewed-by: Mark Zhou <[email protected]>
1 parent b9d83b9 commit 41ff6c3

File tree

2 files changed

+162
-100
lines changed

2 files changed

+162
-100
lines changed

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

Lines changed: 77 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5905,12 +5905,88 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
59055905

59065906
@override
59075907
js_ast.Expression visitInstanceInvocation(InstanceInvocation node) {
5908-
var invocation = _emitInstanceInvocation(node);
5908+
var target = node.interfaceTarget;
5909+
if (target.isPrimitiveOperator) {
5910+
return _emitPrimitiveOperatorInvocation(node);
5911+
}
5912+
var receiver = node.receiver;
5913+
var jsReceiver = _visitExpression(receiver);
5914+
var jsArguments = _emitArgumentList(node.arguments, target: target);
5915+
if (node.isNativeListInvariantAddInvocation(_coreTypes.listClass)) {
5916+
return js.call('#.push(#)', [jsReceiver, jsArguments]);
5917+
}
5918+
var name = node.name.text;
5919+
if (name == 'call') {
5920+
var directCallInvocation = _emitDirectInstanceCallInvocation(node);
5921+
if (directCallInvocation != null) {
5922+
return directCallInvocation;
5923+
}
5924+
}
5925+
if (target.isToStringOrNoSuchMethodWithDefaultSignature &&
5926+
_shouldCallObjectMemberHelper(receiver)) {
5927+
// Handle Object methods when the receiver could potentially be `null` or
5928+
// JavaScript interop values with static helper methods.
5929+
// The names of the static helper methods in the runtime must match the
5930+
// names of the Object instance members.
5931+
return _runtimeCall('#(#, #)', [name, jsReceiver, jsArguments]);
5932+
}
5933+
// Otherwise generate this as a normal typed method call.
5934+
var jsName = _emitMemberName(name, member: target);
5935+
var invocation = js.call('#.#(#)', [jsReceiver, jsName, jsArguments]);
59095936
return _isNullCheckableJsInterop(node.interfaceTarget)
59105937
? _wrapWithJsInteropNullCheck(invocation)
59115938
: invocation;
59125939
}
59135940

5941+
/// Returns a direct invocation to support a `call()` method invocation when
5942+
/// the receiver is considered directly callable, otherwise returns
5943+
/// `null`.
5944+
///
5945+
/// For example if `fn` is statically typed as a function type, then
5946+
/// `fn.call()` would be considered directly callable and compiled as `fn()`.
5947+
js_ast.Expression? _emitDirectInstanceCallInvocation(
5948+
InstanceInvocation node,
5949+
) {
5950+
// Erasing the extension types here to support existing callable behavior
5951+
// on the old style JS interop types that are callable. This should be
5952+
// safe as it is a compile time error to try to dynamically invoke a call
5953+
// method that is inherited from an extension type.
5954+
var receiverType = node.receiver
5955+
.getStaticType(_staticTypeContext)
5956+
.extensionTypeErasure;
5957+
if (!_isDirectCallable(receiverType)) return null;
5958+
// Handle call methods on function types as function calls.
5959+
var invocation = js_ast.Call(
5960+
_visitExpression(node.receiver),
5961+
_emitArgumentList(node.arguments, target: node.interfaceTarget),
5962+
);
5963+
return _isNullCheckableJsInterop(node.interfaceTarget)
5964+
? _wrapWithJsInteropNullCheck(invocation)
5965+
: invocation;
5966+
}
5967+
5968+
/// Returns an invocation of a primitive operator.
5969+
///
5970+
/// See [ProcedureHelpers.isPrimitiveOperator].
5971+
js_ast.Expression _emitPrimitiveOperatorInvocation(InstanceInvocation node) {
5972+
var receiver = node.receiver;
5973+
var target = node.interfaceTarget;
5974+
var arguments = node.arguments;
5975+
assert(arguments.types.isEmpty && arguments.named.isEmpty);
5976+
// JavaScript interop does not support overloading of these operators.
5977+
return switch (arguments.positional.length) {
5978+
0 => _emitUnaryOperator(receiver, target, node),
5979+
1 => _emitBinaryOperator(receiver, target, arguments.positional[0], node),
5980+
// Should always be a compile time error but here for exhaustiveness.
5981+
_ => throw UnsupportedError(
5982+
'Invalid number of positional arguments.\n'
5983+
'Found: ${arguments.positional.length}\n'
5984+
'Operator: ${node.name.text} '
5985+
'${node.location}',
5986+
),
5987+
};
5988+
}
5989+
59145990
@override
59155991
js_ast.Expression visitInstanceGetterInvocation(
59165992
InstanceGetterInvocation node,
@@ -6010,55 +6086,6 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
60106086
], negated: false);
60116087
}
60126088

6013-
js_ast.Expression _emitInstanceInvocation(InstanceInvocation node) {
6014-
var name = node.name.text;
6015-
var receiver = node.receiver;
6016-
var arguments = node.arguments;
6017-
var target = node.interfaceTarget;
6018-
if (target.isPrimitiveOperator && arguments.named.isEmpty) {
6019-
var argLength = arguments.positional.length;
6020-
if (argLength == 0) {
6021-
return _emitUnaryOperator(receiver, target, node);
6022-
} else if (argLength == 1) {
6023-
return _emitBinaryOperator(
6024-
receiver,
6025-
target,
6026-
arguments.positional[0],
6027-
node,
6028-
);
6029-
}
6030-
}
6031-
var jsReceiver = _visitExpression(receiver);
6032-
var args = _emitArgumentList(arguments, target: target);
6033-
if (node.isNativeListInvariantAddInvocation(_coreTypes.listClass)) {
6034-
return js.call('#.push(#)', [jsReceiver, args]);
6035-
}
6036-
if (name == 'call') {
6037-
// Erasing the extension types here to support existing callable behavior
6038-
// on the old style JS interop types that are callable. This should be
6039-
// safe as it is a compile time error to try to dynamically invoke a call
6040-
// method that is inherited from an extension type.
6041-
var receiverType = receiver
6042-
.getStaticType(_staticTypeContext)
6043-
.extensionTypeErasure;
6044-
if (_isDirectCallable(receiverType)) {
6045-
// Handle call methods on function types as function calls.
6046-
return js_ast.Call(jsReceiver, args);
6047-
}
6048-
}
6049-
if (target.isToStringOrNoSuchMethodWithDefaultSignature &&
6050-
_shouldCallObjectMemberHelper(receiver)) {
6051-
// Handle Object methods that are supported by `null` and possibly
6052-
// JavaScript interop values with static helper methods.
6053-
// The names of the static helper methods in the runtime must match the
6054-
// names of the Object instance members.
6055-
return _runtimeCall('#(#, #)', [name, jsReceiver, args]);
6056-
}
6057-
// Otherwise generate this as a normal typed method call.
6058-
var jsName = _emitMemberName(name, member: target);
6059-
return js.call('#.#(#)', [jsReceiver, jsName, args]);
6060-
}
6061-
60626089
/// Returns an invocation of the runtime helpers `dcall`, `dgcall`, `dsend`,
60636090
/// or `dgsend` to perform dynamic checks before invoking [memberName] on
60646091
/// [receiver] and passing [arguments].

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

Lines changed: 85 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6519,12 +6519,96 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
65196519

65206520
@override
65216521
js_ast.Expression visitInstanceInvocation(InstanceInvocation node) {
6522-
var invocation = _emitInstanceInvocation(node);
6522+
var target = node.interfaceTarget;
6523+
if (target.isPrimitiveOperator) {
6524+
return _emitPrimitiveOperatorInvocation(node);
6525+
}
6526+
var receiver = node.receiver;
6527+
var jsReceiver = _visitExpression(receiver);
6528+
var jsArguments = _emitArgumentList(node.arguments, target: target);
6529+
if (node.isNativeListInvariantAddInvocation(_coreTypes.listClass)) {
6530+
// TODO(nshahan): If this code is retained, can it become invalid after a
6531+
// hot reload?
6532+
return js.call('#.push(#)', [jsReceiver, jsArguments]);
6533+
}
6534+
var name = node.name.text;
6535+
if (name == 'call') {
6536+
var directCallInvocation = _emitDirectInstanceCallInvocation(node);
6537+
if (directCallInvocation != null) {
6538+
return directCallInvocation;
6539+
}
6540+
}
6541+
if (target.isToStringOrNoSuchMethodWithDefaultSignature &&
6542+
_shouldCallObjectMemberHelper(receiver)) {
6543+
// Handle Object methods when the receiver could potentially be `null` or
6544+
// JavaScript interop values with static helper methods.
6545+
// The names of the static helper methods in the runtime must match the
6546+
// names of the Object instance members.
6547+
// TODO(nshahan): What should be checked after a hot reload. I think only
6548+
// the return type of the NSM can change.
6549+
return _runtimeCall('#(#, #)', [name, jsReceiver, jsArguments]);
6550+
}
6551+
// Otherwise generate this as a normal typed method call.
6552+
var jsName = _emitMemberName(name, member: target);
6553+
var invocation = js.call('#.#(#)', [jsReceiver, jsName, jsArguments]);
65236554
return _isNullCheckableJsInterop(node.interfaceTarget)
65246555
? _wrapWithJsInteropNullCheck(invocation)
65256556
: invocation;
65266557
}
65276558

6559+
/// Returns a direct invocation to support a `call()` method invocation when
6560+
/// the receiver is considered directly callable, otherwise returns
6561+
/// `null`.
6562+
///
6563+
/// For example if `fn` is statically typed as a function type, then
6564+
/// `fn.call()` would be considered directly callable and compiled as `fn()`.
6565+
// TODO(nshahan): Handle retained call method invocations that have new
6566+
// signatures or were deleted after a hot reload.
6567+
js_ast.Expression? _emitDirectInstanceCallInvocation(
6568+
InstanceInvocation node,
6569+
) {
6570+
// Erasing the extension types here to support existing callable behavior
6571+
// on the old style JS interop types that are callable. This should be
6572+
// safe as it is a compile time error to try to dynamically invoke a call
6573+
// method that is inherited from an extension type.
6574+
var receiverType = node.receiver
6575+
.getStaticType(_staticTypeContext)
6576+
.extensionTypeErasure;
6577+
if (!_isDirectCallable(receiverType)) return null;
6578+
// Handle call methods on function types as function calls.
6579+
var invocation = js_ast.Call(
6580+
_visitExpression(node.receiver),
6581+
_emitArgumentList(node.arguments, target: node.interfaceTarget),
6582+
);
6583+
return _isNullCheckableJsInterop(node.interfaceTarget)
6584+
? _wrapWithJsInteropNullCheck(invocation)
6585+
: invocation;
6586+
}
6587+
6588+
/// Returns an invocation of a primitive operator.
6589+
///
6590+
/// See [ProcedureHelpers.isPrimitiveOperator].
6591+
// TODO(nshahan): Handle retained operator invocations that have new
6592+
// signatures or were deleted after a hot reload.
6593+
js_ast.Expression _emitPrimitiveOperatorInvocation(InstanceInvocation node) {
6594+
var receiver = node.receiver;
6595+
var target = node.interfaceTarget;
6596+
var arguments = node.arguments;
6597+
assert(arguments.types.isEmpty && arguments.named.isEmpty);
6598+
// JavaScript interop does not support overloading of these operators.
6599+
return switch (arguments.positional.length) {
6600+
0 => _emitUnaryOperator(receiver, target, node),
6601+
1 => _emitBinaryOperator(receiver, target, arguments.positional[0], node),
6602+
// Should always be a compile time error but here for exhaustiveness.
6603+
_ => throw UnsupportedError(
6604+
'Invalid number of positional arguments.\n'
6605+
'Found: ${arguments.positional.length}\n'
6606+
'Operator: ${node.name.text} '
6607+
'${node.location}',
6608+
),
6609+
};
6610+
}
6611+
65286612
@override
65296613
js_ast.Expression visitInstanceGetterInvocation(
65306614
InstanceGetterInvocation node,
@@ -6624,55 +6708,6 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
66246708
], negated: false);
66256709
}
66266710

6627-
js_ast.Expression _emitInstanceInvocation(InstanceInvocation node) {
6628-
var name = node.name.text;
6629-
var receiver = node.receiver;
6630-
var arguments = node.arguments;
6631-
var target = node.interfaceTarget;
6632-
if (target.isPrimitiveOperator && arguments.named.isEmpty) {
6633-
var argLength = arguments.positional.length;
6634-
if (argLength == 0) {
6635-
return _emitUnaryOperator(receiver, target, node);
6636-
} else if (argLength == 1) {
6637-
return _emitBinaryOperator(
6638-
receiver,
6639-
target,
6640-
arguments.positional[0],
6641-
node,
6642-
);
6643-
}
6644-
}
6645-
var jsReceiver = _visitExpression(receiver);
6646-
var args = _emitArgumentList(arguments, target: target);
6647-
if (node.isNativeListInvariantAddInvocation(_coreTypes.listClass)) {
6648-
return js.call('#.push(#)', [jsReceiver, args]);
6649-
}
6650-
if (name == 'call') {
6651-
// Erasing the extension types here to support existing callable behavior
6652-
// on the old style JS interop types that are callable. This should be
6653-
// safe as it is a compile time error to try to dynamically invoke a call
6654-
// method that is inherited from an extension type.
6655-
var receiverType = receiver
6656-
.getStaticType(_staticTypeContext)
6657-
.extensionTypeErasure;
6658-
if (_isDirectCallable(receiverType)) {
6659-
// Handle call methods on function types as function calls.
6660-
return js_ast.Call(jsReceiver, args);
6661-
}
6662-
}
6663-
if (target.isToStringOrNoSuchMethodWithDefaultSignature &&
6664-
_shouldCallObjectMemberHelper(receiver)) {
6665-
// Handle Object methods when the receiver could potentially be `null` or
6666-
// JavaScript interop values with static helper methods.
6667-
// The names of the static helper methods in the runtime must match the
6668-
// names of the Object instance members.
6669-
return _runtimeCall('#(#, #)', [name, jsReceiver, args]);
6670-
}
6671-
// Otherwise generate this as a normal typed method call.
6672-
var jsName = _emitMemberName(name, member: target);
6673-
return js.call('#.#(#)', [jsReceiver, jsName, args]);
6674-
}
6675-
66766711
/// Returns an invocation of the runtime helpers `dcall`, `dgcall`, `dsend`,
66776712
/// or `dgsend` to perform dynamic checks before invoking [memberName] on
66786713
/// [receiver] and passing [arguments].

0 commit comments

Comments
 (0)