Handle effectively constant fields in class stub generator
Closes #36222 Change-Id: Ic8b7a3963e1364903b4b2d7871ff0b12cb134572 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97082 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
efe9366eeb
commit
eb6f1b6abd
@@ -9,6 +9,7 @@ import '../common_elements.dart' show CommonElements;
|
||||
import '../elements/entities.dart';
|
||||
import '../js/js.dart' as jsAst;
|
||||
import '../js/js.dart' show js;
|
||||
import '../js_backend/field_analysis.dart';
|
||||
import '../js_backend/namer.dart' show Namer;
|
||||
import '../js_backend/interceptor_data.dart' show InterceptorData;
|
||||
import '../options.dart';
|
||||
@@ -108,8 +109,14 @@ class ClassStubGenerator {
|
||||
}
|
||||
return js('#.#()', [receiver, getterName]);
|
||||
} else {
|
||||
jsAst.Name fieldName = _namer.instanceFieldPropertyName(member);
|
||||
return js('#.#', [receiver, fieldName]);
|
||||
FieldAnalysisData fieldData =
|
||||
_closedWorld.fieldAnalysis.getFieldData(member);
|
||||
if (fieldData.isEffectivelyConstant) {
|
||||
return _emitter.constantReference(fieldData.constantValue);
|
||||
} else {
|
||||
jsAst.Name fieldName = _namer.instanceFieldPropertyName(member);
|
||||
return js('#.#', [receiver, fieldName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
/*element: method1:params=0*/
|
||||
@pragma('dart2js:noInline')
|
||||
method1([a]) => /*params=0*/ () => a;
|
||||
method1([a]) => /*access=[a],params=0*/ () => a;
|
||||
|
||||
class Class {
|
||||
/*element: Class.f:emitted*/
|
||||
@@ -13,14 +13,14 @@ class Class {
|
||||
|
||||
/*element: Class.capture:params=0*/
|
||||
@pragma('dart2js:noInline')
|
||||
Class.capture([a]) : f = (/*params=0*/ () => a);
|
||||
Class.capture([a]) : f = (/*access=[a],params=0*/ () => a);
|
||||
|
||||
// TODO(johnniwinther): Remove the redundant assignment of elided boxed
|
||||
// parameters.
|
||||
/*element: Class.box:assign=[a,a],params=0*/
|
||||
@pragma('dart2js:noInline')
|
||||
Class.box([a])
|
||||
: f = (/*assign=[a],params=0*/ () {
|
||||
: f = (/*access=[_box_0],assign=[a],params=0*/ () {
|
||||
a = 42;
|
||||
});
|
||||
|
||||
@@ -30,12 +30,12 @@ class Class {
|
||||
class Subclass extends Class {
|
||||
/*element: Subclass.capture:params=0*/
|
||||
@pragma('dart2js:noInline')
|
||||
Subclass.capture([a]) : super.internal(/*params=0*/ () => a);
|
||||
Subclass.capture([a]) : super.internal(/*access=[a],params=0*/ () => a);
|
||||
|
||||
/*element: Subclass.box:assign=[a,a],params=0*/
|
||||
@pragma('dart2js:noInline')
|
||||
Subclass.box([a])
|
||||
: super.internal(/*assign=[a],params=0*/ () {
|
||||
: super.internal(/*access=[_box_0],assign=[a],params=0*/ () {
|
||||
a = 42;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
typedef int BinaryFunc(int x, int y);
|
||||
|
||||
class A {
|
||||
const A({this.foo = A.defaultFoo});
|
||||
|
||||
/*element: A.defaultFoo:params=2*/
|
||||
static int defaultFoo(int x, int y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
/*element: A.foo:elided,stubCalls=[foo$2:call$2(arg0,arg1),foo$2:main_A_defaultFoo$closure(0)]*/
|
||||
final BinaryFunc foo;
|
||||
}
|
||||
|
||||
/*element: test:calls=[foo$2(2)],params=1*/
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
@pragma('dart2js:noInline')
|
||||
test(dynamic a) => a.foo(1, 2);
|
||||
|
||||
/*element: main:calls=[test(1)],params=0*/
|
||||
main() {
|
||||
test(new A());
|
||||
}
|
||||
@@ -59,6 +59,8 @@ class Tags {
|
||||
static const String parameterCount = 'params';
|
||||
static const String call = 'calls';
|
||||
static const String parameterStub = 'stubs';
|
||||
static const String callStubCall = 'stubCalls';
|
||||
static const String callStubAccesses = 'stubAccesses';
|
||||
static const String isEmitted = 'emitted';
|
||||
static const String isElided = 'elided';
|
||||
static const String assignment = 'assign';
|
||||
@@ -83,6 +85,75 @@ class ModelIrComputer extends IrDataExtractor<Features> {
|
||||
: _programLookup = new ProgramLookup(compiler),
|
||||
super(reporter, actualMap);
|
||||
|
||||
void registerCalls(Features features, String tag, js.Node node,
|
||||
{String prefix = '', Set<js.PropertyAccess> handledAccesses}) {
|
||||
forEachNode(node, onCall: (js.Call node) {
|
||||
js.Node target = node.target;
|
||||
if (target is js.PropertyAccess) {
|
||||
js.Node selector = target.selector;
|
||||
bool fixedNameCall = false;
|
||||
String name;
|
||||
if (selector is js.Name) {
|
||||
name = selector.key;
|
||||
fixedNameCall = selector is StringBackedName;
|
||||
} else if (selector is js.LiteralString) {
|
||||
/// Call to fixed backend name, so we include the argument
|
||||
/// values to test encoding of optional parameters in native
|
||||
/// methods.
|
||||
name = selector.value.substring(1, selector.value.length - 1);
|
||||
fixedNameCall = true;
|
||||
}
|
||||
if (name != null) {
|
||||
if (fixedNameCall) {
|
||||
String arguments = node.arguments.map(js.nodeToString).join(',');
|
||||
features.addElement(tag, '${prefix}${name}(${arguments})');
|
||||
} else {
|
||||
features.addElement(
|
||||
tag, '${prefix}${name}(${node.arguments.length})');
|
||||
}
|
||||
handledAccesses?.add(target);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void registerAccesses(Features features, String tag, js.Node code,
|
||||
{String prefix = '', Set<js.PropertyAccess> handledAccesses}) {
|
||||
forEachNode(code, onPropertyAccess: (js.PropertyAccess node) {
|
||||
if (handledAccesses?.contains(node) ?? false) {
|
||||
return;
|
||||
}
|
||||
|
||||
js.Node receiver = node.receiver;
|
||||
String receiverName;
|
||||
if (receiver is js.VariableUse) {
|
||||
receiverName = receiver.name;
|
||||
if (receiverName == receiverName.toUpperCase() &&
|
||||
receiverName != r'$') {
|
||||
// Skip holder access.
|
||||
receiverName = null;
|
||||
}
|
||||
} else if (receiver is js.This) {
|
||||
receiverName = 'this';
|
||||
}
|
||||
|
||||
js.Node selector = node.selector;
|
||||
String name;
|
||||
if (selector is js.Name) {
|
||||
name = selector.key;
|
||||
} else if (selector is js.LiteralString) {
|
||||
/// Call to fixed backend name, so we include the argument
|
||||
/// values to test encoding of optional parameters in native
|
||||
/// methods.
|
||||
name = selector.value.substring(1, selector.value.length - 1);
|
||||
}
|
||||
|
||||
if (receiverName != null && name != null) {
|
||||
features.addElement(tag, '${prefix}${name}');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Features getMemberValue(MemberEntity member) {
|
||||
if (member is FieldEntity) {
|
||||
Field field = _programLookup.getField(member);
|
||||
@@ -115,6 +186,16 @@ class ModelIrComputer extends IrDataExtractor<Features> {
|
||||
registerFlags(Tags.getterFlags, field.getterFlags);
|
||||
registerFlags(Tags.setterFlags, field.setterFlags);
|
||||
|
||||
Class cls = _programLookup.getClass(member.enclosingClass);
|
||||
for (StubMethod stub in cls.callStubs) {
|
||||
if (stub.element == member) {
|
||||
registerCalls(features, Tags.callStubCall, stub.code,
|
||||
prefix: '${stub.name.key}:');
|
||||
registerAccesses(features, Tags.callStubAccesses, stub.code,
|
||||
prefix: '${stub.name.key}:');
|
||||
}
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
StaticField staticField = _programLookup.getStaticField(member);
|
||||
@@ -137,42 +218,12 @@ class ModelIrComputer extends IrDataExtractor<Features> {
|
||||
|
||||
Set<js.PropertyAccess> handledAccesses = new Set();
|
||||
|
||||
void registerCalls(String tag, js.Node node, [String prefix = '']) {
|
||||
forEachNode(node, onCall: (js.Call node) {
|
||||
js.Node target = node.target;
|
||||
if (target is js.PropertyAccess) {
|
||||
js.Node selector = target.selector;
|
||||
bool fixedNameCall = false;
|
||||
String name;
|
||||
if (selector is js.Name) {
|
||||
name = selector.key;
|
||||
fixedNameCall = selector is StringBackedName;
|
||||
} else if (selector is js.LiteralString) {
|
||||
/// Call to fixed backend name, so we include the argument
|
||||
/// values to test encoding of optional parameters in native
|
||||
/// methods.
|
||||
name = selector.value.substring(1, selector.value.length - 1);
|
||||
fixedNameCall = true;
|
||||
}
|
||||
if (name != null) {
|
||||
if (fixedNameCall) {
|
||||
String arguments =
|
||||
node.arguments.map(js.nodeToString).join(',');
|
||||
features.addElement(tag, '${prefix}${name}(${arguments})');
|
||||
} else {
|
||||
features.addElement(
|
||||
tag, '${prefix}${name}(${node.arguments.length})');
|
||||
}
|
||||
handledAccesses.add(target);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
registerCalls(Tags.call, code);
|
||||
registerCalls(features, Tags.call, code,
|
||||
handledAccesses: handledAccesses);
|
||||
if (method is DartMethod) {
|
||||
for (ParameterStubMethod stub in method.parameterStubs) {
|
||||
registerCalls(Tags.parameterStub, stub.code, '${stub.name.key}:');
|
||||
registerCalls(features, Tags.parameterStub, stub.code,
|
||||
prefix: '${stub.name.key}:', handledAccesses: handledAccesses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,37 +244,8 @@ class ModelIrComputer extends IrDataExtractor<Features> {
|
||||
}
|
||||
});
|
||||
|
||||
forEachNode(code, onPropertyAccess: (js.PropertyAccess node) {
|
||||
if (handledAccesses.contains(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
js.Node receiver = node.receiver;
|
||||
String receiverName;
|
||||
if (receiver is js.VariableUse) {
|
||||
receiverName = receiver.name;
|
||||
if (receiverName == receiverName.toUpperCase() &&
|
||||
receiverName != r'$') {
|
||||
// Skip holder access.
|
||||
receiverName = null;
|
||||
}
|
||||
}
|
||||
|
||||
js.Node selector = node.selector;
|
||||
String name;
|
||||
if (selector is js.Name) {
|
||||
name = selector.key;
|
||||
} else if (selector is js.LiteralString) {
|
||||
/// Call to fixed backend name, so we include the argument
|
||||
/// values to test encoding of optional parameters in native
|
||||
/// methods.
|
||||
name = selector.value.substring(1, selector.value.length - 1);
|
||||
}
|
||||
|
||||
if (receiverName != null && name != null) {
|
||||
features.addElement(Tags.propertyAccess, '${name}');
|
||||
}
|
||||
});
|
||||
registerAccesses(features, Tags.propertyAccess, code,
|
||||
handledAccesses: handledAccesses);
|
||||
|
||||
forEachNode(code, onSwitch: (js.Switch node) {
|
||||
features.add(Tags.switchCase);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
typedef int BinaryFunc(int x, int y);
|
||||
|
||||
class A {
|
||||
const A({this.foo = A.defaultFoo});
|
||||
|
||||
static int defaultFoo(int x, int y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
/*element: A.foo:constant=FunctionConstant(A.defaultFoo)*/
|
||||
final BinaryFunc foo;
|
||||
}
|
||||
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
@pragma('dart2js:noInline')
|
||||
test(dynamic a) => a.foo(1, 2);
|
||||
|
||||
main() {
|
||||
test(new A());
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
typedef int BinaryFunc(int x, int y);
|
||||
|
||||
class A {
|
||||
const A({this.foo = A.defaultFoo});
|
||||
|
||||
static int defaultFoo(int x, int y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
/*element: A.foo:A.=foo:FunctionConstant(A.defaultFoo),initial=NullConstant*/
|
||||
final BinaryFunc foo;
|
||||
}
|
||||
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
@pragma('dart2js:noInline')
|
||||
test(dynamic a) => a.foo(1, 2);
|
||||
|
||||
main() {
|
||||
test(new A());
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
typedef int BinaryFunc(int x, int y);
|
||||
|
||||
class A {
|
||||
const A({this.foo = A.defaultFoo});
|
||||
|
||||
static int defaultFoo(int x, int y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
final BinaryFunc foo;
|
||||
}
|
||||
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
@pragma('dart2js:noInline')
|
||||
test(dynamic a) => a.foo(1, 2);
|
||||
|
||||
main() {
|
||||
test(new A());
|
||||
}
|
||||
Reference in New Issue
Block a user