sdk
sdk copied to clipboard
[dart2js] Bug in inferred type of static closure `call` method tear-off
Consider
import 'dart:typed_data';
String foo() => "Lily was here";
main() {
var f = foo;
var x = f.call;
(print)("Lily was here" == x());
//(print)(Uint8List(0));
}
Running:
dart compile js foo.dart --out=o1.js && v8 o1.js:
true
Uncomment the last line in main, and run again:
false
[]
It appears that adding a live @Native class causes the inferred type of the tear-off to be Never.
The difference is in the computed type of the static method, and a bug thereafter:
resultTypeOfSelector([subclass=Closure], get:call) --> any
resultTypeOfSelector([subtype=Function], get:call) --> empty
closedWorld.includesClosureCall and closedWorld.locateMembers produce the wrong result for [subtype=Function].
bool includesClosureCallInDomain(
Selector selector,
AbstractValue? receiver,
AbstractValueDomain abstractValueDomain,
) {
return selector.name == Identifiers.call &&
(receiver == null ||
// This is logically equivalent to the former implementation using
// `abstractValueDomain.contains` (which wrapped `containsMask`).
// The switch to `abstractValueDomain.containsType` is because
// `contains` was generally unsound but happened to work correctly
// here. See https://dart-review.googlesource.com/c/sdk/+/130565
// for further discussion.
//
// This checks if the receiver mask contains the entire type cone
// originating from [_functionLub] and may therefore be unsound if
// the receiver mask contains only part of the type cone. (Is this
// possible?)
//
// TODO(fishythefish): Use `isDisjoint` or equivalent instead of
// `containsType` once we can ensure it's fast enough.
abstractValueDomain
.containsType(receiver, _functionLub)
.isPotentiallyTrue);
}
This returns false when receiver=[subtype=Function] and _functionLub=j:class(Function).
The comment '(Is this possible?)' will soon be 'yes':
I want to strengthen the receiver type with the static type from the Kernel IR for closure calls as this is one of the few remaining sources of primitives checks. To get to the point where closure calls can be trusted, we will need to use '[subclass=Closure]' in more places in order to exclude calls to the one other class that implements Function: JavaScriptFunction. (A test case for this is newtonsMethod in the Flutter engine and flute benchmark. It calls a closure in a loop, so we can't afford to get rid of primitive checks by replacing them with dynamic calls).