Recognize and inline effectively constant fields

Change-Id: I243ee80ebc45d9cdab2f7c944cf5eceb26185b2c
Reviewed-on: https://dart-review.googlesource.com/c/93436
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
This commit is contained in:
Johnni Winther 2019-02-22 14:06:05 +00:00 committed by commit-bot@chromium.org
parent db62dc5323
commit 60e4f194b0
41 changed files with 952 additions and 182 deletions

View file

@ -443,6 +443,8 @@ class DartConstantSystem extends ConstantSystem {
final round = const UnfoldedUnaryOperation('round');
final abs = const UnfoldedUnaryOperation('abs');
// TODO(johnniwinther): Delete this (embed parts into JavaScript constant
// system where needed).
const DartConstantSystem();
@override
@ -475,7 +477,7 @@ class DartConstantSystem extends ConstantSystem {
@override
MapConstantValue createMap(CommonElements commonElements, InterfaceType type,
List<ConstantValue> keys, List<ConstantValue> values) {
return new MapConstantValue(type, keys, values);
throw new UnsupportedError('DartConstantSystem.createMap');
}
@override

View file

@ -582,7 +582,7 @@ class SetConstantValue extends ObjectConstantValue {
ConstantValueKind get kind => ConstantValueKind.SET;
}
class MapConstantValue extends ObjectConstantValue {
abstract class MapConstantValue extends ObjectConstantValue {
final List<ConstantValue> keys;
final List<ConstantValue> values;
final int hashCode;

View file

@ -10,6 +10,7 @@ import '../diagnostics/diagnostic_listener.dart';
import '../diagnostics/messages.dart';
import '../elements/entities.dart';
import '../kernel/dart2js_target.dart';
import '../options.dart';
import '../serialization/serialization.dart';
import '../util/enumset.dart';
@ -95,6 +96,7 @@ class PragmaAnnotation {
}
Set<PragmaAnnotation> processMemberAnnotations(
CompilerOptions options,
DiagnosticReporter reporter,
KCommonElements commonElements,
KElementEnvironment elementEnvironment,
@ -107,7 +109,8 @@ Set<PragmaAnnotation> processMemberAnnotations(
}
LibraryEntity library = element.library;
bool platformAnnotationsAllowed = library.canonicalUri.scheme == 'dart' ||
bool platformAnnotationsAllowed = options.testMode ||
library.canonicalUri.scheme == 'dart' ||
maybeEnableNative(library.canonicalUri);
for (ConstantValue constantValue

View file

@ -182,9 +182,12 @@ class JFieldAnalysis implements FieldAnalysis {
// --csp and --fast-startup have different constraints to the generated code.
final Map<FieldEntity, ConstantValue> _fixedInitializers;
final Map<FieldEntity, ConstantValue> _effectivelyConstantFields;
final Set<FieldEntity> _elidedFields;
JFieldAnalysis._(this._fixedInitializers, this._elidedFields);
JFieldAnalysis._(this._fixedInitializers, this._effectivelyConstantFields,
this._elidedFields);
/// Deserializes a [JFieldAnalysis] object from [source].
factory JFieldAnalysis.readFromDataSource(
@ -192,15 +195,19 @@ class JFieldAnalysis implements FieldAnalysis {
source.begin(tag);
Map<FieldEntity, ConstantValue> fixedInitializers =
source.readMemberMap(source.readConstant);
Map<FieldEntity, ConstantValue> effectivelyConstantFields =
source.readMemberMap(source.readConstant);
Set<FieldEntity> elidedFields = source.readMembers<FieldEntity>().toSet();
source.end(tag);
return new JFieldAnalysis._(fixedInitializers, elidedFields);
return new JFieldAnalysis._(
fixedInitializers, effectivelyConstantFields, elidedFields);
}
/// Serializes this [JFieldAnalysis] to [sink].
void writeToDataSink(DataSink sink) {
sink.begin(tag);
sink.writeMemberMap(_fixedInitializers, sink.writeConstant);
sink.writeMemberMap(_effectivelyConstantFields, sink.writeConstant);
sink.writeMembers(_elidedFields);
sink.end(tag);
}
@ -208,39 +215,64 @@ class JFieldAnalysis implements FieldAnalysis {
factory JFieldAnalysis.from(
KClosedWorld closedWorld, JsToFrontendMap map, CompilerOptions options) {
Map<FieldEntity, ConstantValue> fixedInitializers = {};
Map<FieldEntity, ConstantValue> effectivelyConstantFields = {};
Set<FieldEntity> elidedFields = new Set();
bool canBeElided(FieldEntity field) {
return !closedWorld.annotationsData.hasNoElision(field) &&
!closedWorld.nativeData.isNativeMember(field);
}
closedWorld.fieldAnalysis._fixedInitializers
.forEach((KField kField, AllocatorData data) {
// TODO(johnniwinther): Use liveness of constructors and elided optional
// parameters to recognize more constant initializers.
if (data.initialValue != null && data.initializers.isEmpty) {
ConstantValue value = data.initialValue;
if (value.isNull || value.isInt || value.isBool || value.isString) {
// TODO(johnniwinther,sra): Support non-primitive constants in
// allocators when it does cause allocators to deoptimized because
// of deferred loading.
JField jField = map.toBackendMember(kField);
if (jField == null) {
return;
}
JField jField = map.toBackendMember(kField);
if (jField != null) {
fixedInitializers[jField] = map.toBackendConstant(value);
// TODO(johnniwinther): Should elided static fields be removed from the
// J model? Static setters might still assign to them.
MemberUsage memberUsage = closedWorld.liveMemberUsage[kField];
if (!memberUsage.hasRead) {
if (canBeElided(kField)) {
elidedFields.add(jField);
}
} else {
// TODO(johnniwinther): Use liveness of constructors and elided optional
// parameters to recognize more constant initializers.
if (data.initialValue != null && data.initializers.isEmpty) {
ConstantValue value = map.toBackendConstant(data.initialValue);
assert(value != null);
if (!memberUsage.hasWrite && canBeElided(kField)) {
elidedFields.add(jField);
effectivelyConstantFields[jField] = value;
} else if (value.isNull ||
value.isInt ||
value.isBool ||
value.isString) {
// TODO(johnniwinther,sra): Support non-primitive constants in
// allocators when it does cause allocators to deoptimized because
// of deferred loading.
fixedInitializers[jField] = value;
}
}
}
});
Set<FieldEntity> elidedFields = new Set();
// TODO(johnniwinther): Recognize effectively constant top level/static
// fields.
closedWorld.liveMemberUsage
.forEach((MemberEntity member, MemberUsage memberUsage) {
// TODO(johnniwinther): Should elided static fields be removed from the
// J model? Static setters might still assign to them.
if (member.isField &&
!memberUsage.hasRead &&
!closedWorld.annotationsData.hasNoElision(member) &&
!closedWorld.nativeData.isNativeMember(member)) {
elidedFields.add(map.toBackendMember(member));
if (member.isField && !member.isInstanceMember) {
if (!memberUsage.hasRead && canBeElided(member)) {
elidedFields.add(map.toBackendMember(member));
}
}
});
return new JFieldAnalysis._(fixedInitializers, elidedFields);
return new JFieldAnalysis._(
fixedInitializers, effectivelyConstantFields, elidedFields);
}
// TODO(sra): Add way to let injected fields be initialized to a constant in
@ -254,18 +286,23 @@ class JFieldAnalysis implements FieldAnalysis {
/// Return the constant for a field initialized in allocator. Returns `null`
/// for fields not initialized in allocator.
ConstantValue initializerValue(JField field) {
assert(isInitializedInAllocator(field));
return _fixedInitializers[field];
}
/// Returns `true` if [field] can be elided from the output.
///
/// This happens if a field is written to but never read.
// TODO(johnniwinther): Include fields that are effectively final.
bool isElided(JField field) => _elidedFields.contains(field);
/// Returns `true` if [field] is effectively constant and therefore only
/// holds its [initializerValue].
// TODO(johnniwinther): Recognize fields that are initialized to a constant
// but never written to.
bool isEffectivelyConstant(JField field) => false;
bool isEffectivelyConstant(JField field) =>
_effectivelyConstantFields.containsKey(field);
/// Returns the [ConstantValue] for the effectively constant [field].
ConstantValue getConstantValue(JField field) {
assert(isEffectivelyConstant(field));
return _effectivelyConstantFields[field];
}
}

View file

@ -574,7 +574,7 @@ class Namer {
}
/// Used to disambiguate names for constants in [constantName].
final NamingScope constantScope = new NamingScope();
final NamingScope _constantScope = new NamingScope();
/// Used to store scopes for instances of [PrivatelyNamedJsEntity]
final Map<Entity, NamingScope> _privateNamingScopes = {};
@ -583,8 +583,8 @@ class Namer {
final Map<LibraryEntity, String> libraryLongNames = HashMap();
final Map<ConstantValue, jsAst.Name> constantNames = HashMap();
final Map<ConstantValue, String> constantLongNames = {};
final Map<ConstantValue, jsAst.Name> _constantNames = HashMap();
final Map<ConstantValue, String> _constantLongNames = {};
ConstantCanonicalHasher _constantHasher;
/// Maps private names to a library that may use that name without prefixing
@ -718,25 +718,25 @@ class Namer {
// function constants since the function-implementation itself serves as
// constant and can be accessed directly.
assert(!constant.isFunction);
jsAst.Name result = constantNames[constant];
jsAst.Name result = _constantNames[constant];
if (result == null) {
String longName = constantLongName(constant);
result = getFreshName(constantScope, longName);
constantNames[constant] = result;
result = getFreshName(_constantScope, longName);
_constantNames[constant] = result;
}
return _newReference(result);
}
/// Proposed name for [constant].
String constantLongName(ConstantValue constant) {
String longName = constantLongNames[constant];
String longName = _constantLongNames[constant];
if (longName == null) {
_constantHasher ??=
new ConstantCanonicalHasher(rtiEncoder, _codegenWorldBuilder);
longName = new ConstantNamingVisitor(
rtiEncoder, _codegenWorldBuilder, _constantHasher)
.getName(constant);
constantLongNames[constant] = longName;
_constantLongNames[constant] = longName;
}
return longName;
}

View file

@ -384,6 +384,8 @@ class Field {
final ConstantValue initializerInAllocator;
final ConstantValue constantValue;
final bool isElided;
// TODO(floitsch): support renamed fields.
@ -395,6 +397,7 @@ class Field {
this.setterFlags,
this.needsCheckedSetter,
this.initializerInAllocator,
this.constantValue,
this.isElided);
bool get needsGetter => getterFlags != 0;

View file

@ -1071,10 +1071,14 @@ class ProgramBuilder {
}
}
ConstantValue initializerInAllocator = null;
ConstantValue initializerInAllocator;
if (_allocatorAnalysis.isInitializedInAllocator(field)) {
initializerInAllocator = _allocatorAnalysis.initializerValue(field);
}
ConstantValue constantValue;
if (_allocatorAnalysis.isEffectivelyConstant(field)) {
constantValue = _allocatorAnalysis.getConstantValue(field);
}
fields.add(new Field(
field,
@ -1084,6 +1088,7 @@ class ProgramBuilder {
setterFlags,
needsCheckedSetter,
initializerInAllocator,
constantValue,
_closedWorld.fieldAnalysis.isElided(field)));
}

View file

@ -1078,17 +1078,33 @@ class FragmentEmitter {
Method generateGetter(Field field) {
assert(field.needsGetter);
String template;
if (field.needsInterceptedGetterOnReceiver) {
template = "function(receiver) { return receiver[#]; }";
} else if (field.needsInterceptedGetterOnThis) {
template = "function(receiver) { return this[#]; }";
js.Expression code;
if (field.isElided) {
ConstantValue constantValue = field.constantValue;
if (constantValue == null) {
assert(_closedWorld.abstractValueDomain is TrivialAbstractValueDomain);
// Since static types are not used in invocation/access of instance
// members in codegen, we see dynamic uses in codegen that are not
// present in resolution when using the trivial abstract value domain.
// This means that resolution can determine that a field is never read
// but codegen thinks it is read.
constantValue = new NullConstantValue();
}
code = js.js(
"function() { return #; }", generateConstantReference(constantValue));
} else {
assert(!field.needsInterceptedGetter);
template = "function() { return this[#]; }";
String template;
if (field.needsInterceptedGetterOnReceiver) {
template = "function(receiver) { return receiver[#]; }";
} else if (field.needsInterceptedGetterOnThis) {
template = "function(receiver) { return this[#]; }";
} else {
assert(!field.needsInterceptedGetter);
template = "function() { return this[#]; }";
}
js.Expression fieldName = js.quoteName(field.name);
code = js.js(template, fieldName);
}
js.Expression fieldName = js.quoteName(field.name);
js.Expression code = js.js(template, fieldName);
js.Name getterName = namer.deriveGetterName(field.accessorName);
return new StubMethod(getterName, code);
}

View file

@ -32,10 +32,12 @@ import 'package:js_ast/src/precedence.dart' as js_precedence;
import '../../../compiler_new.dart';
import '../../common.dart';
import '../../compiler.dart' show Compiler;
import '../../constants/values.dart' show ConstantValue, FunctionConstantValue;
import '../../constants/values.dart'
show ConstantValue, FunctionConstantValue, NullConstantValue;
import '../../common_elements.dart' show CommonElements;
import '../../elements/entities.dart';
import '../../hash/sha1.dart' show Hasher;
import '../../inferrer/trivial.dart';
import '../../io/code_output.dart';
import '../../io/location_provider.dart' show LocationCollector;
import '../../io/source_map_builder.dart' show SourceMapBuilder;

View file

@ -16,8 +16,9 @@ import '../elements/types.dart';
import '../inferrer/abstract_value_domain.dart';
import '../ir/closure.dart';
import '../js_backend/annotations.dart';
import '../js_backend/field_analysis.dart';
import '../js_backend/backend_usage.dart';
import '../js_backend/constant_system_javascript.dart';
import '../js_backend/field_analysis.dart';
import '../js_backend/interceptor_data.dart';
import '../js_backend/native_data.dart';
import '../js_backend/no_such_method_registry.dart';
@ -859,16 +860,19 @@ class _ConstantConverter implements ConstantValueVisitor<ConstantValue, Null> {
return new SetConstantValue(type, values);
}
ConstantValue visitMap(MapConstantValue constant, _) {
var type = typeConverter.visit(constant.type, toBackendEntity);
List<ConstantValue> keys = _handleValues(constant.keys);
ConstantValue visitMap(covariant JavaScriptMapConstant constant, _) {
DartType type = typeConverter.visit(constant.type, toBackendEntity);
ListConstantValue keys = constant.keyList.accept(this, null);
List<ConstantValue> values = _handleValues(constant.values);
ConstantValue protoValue = constant.protoValue?.accept(this, null);
if (identical(keys, constant.keys) &&
identical(values, constant.values) &&
type == constant.type) {
type == constant.type &&
protoValue == constant.protoValue) {
return constant;
}
return new MapConstantValue(type, keys, values);
return new JavaScriptMapConstant(
type, keys, values, protoValue, constant.onlyStringKeys);
}
ConstantValue visitConstructed(ConstructedConstantValue constant, _) {

View file

@ -227,6 +227,7 @@ class KernelWorkItem implements WorkItem {
return _compilerTask.measure(() {
_nativeMemberResolver.resolveNativeMember(element);
Set<PragmaAnnotation> annotations = processMemberAnnotations(
_elementMap.options,
_elementMap.reporter,
_elementMap.commonElements,
_elementMap.elementEnvironment,

View file

@ -401,10 +401,12 @@ abstract class AbstractDataSink extends DataSinkMixin implements DataSink {
writeConstants(constant.values);
break;
case ConstantValueKind.MAP:
MapConstantValue constant = value;
JavaScriptMapConstant constant = value;
writeDartType(constant.type);
writeConstants(constant.keys);
writeConstant(constant.keyList);
writeConstants(constant.values);
writeConstantOrNull(constant.protoValue);
writeBool(constant.onlyStringKeys);
break;
case ConstantValueKind.CONSTRUCTED:
ConstructedConstantValue constant = value;

View file

@ -463,9 +463,12 @@ abstract class AbstractDataSource extends DataSourceMixin
return new SetConstantValue(type, values);
case ConstantValueKind.MAP:
DartType type = readDartType();
List<ConstantValue> keys = readConstants();
ListConstantValue keyList = readConstant();
List<ConstantValue> values = readConstants();
return new MapConstantValue(type, keys, values);
ConstantValue protoValue = readConstantOrNull();
bool onlyStringKeys = readBool();
return new JavaScriptMapConstant(
type, keyList, values, protoValue, onlyStringKeys);
case ConstantValueKind.CONSTRUCTED:
InterfaceType type = readDartType();
Map<FieldEntity, ConstantValue> fields =

View file

@ -255,6 +255,15 @@ abstract class DataSourceMixin implements DataSource {
return null;
}
@override
ConstantValue readConstantOrNull() {
bool hasClass = readBool();
if (hasClass) {
return readConstant();
}
return null;
}
@override
List<E> readConstants<E extends ConstantValue>({bool emptyAsNull: false}) {
int count = readInt();
@ -610,6 +619,14 @@ abstract class DataSinkMixin implements DataSink {
}
}
@override
void writeConstantOrNull(ConstantValue value) {
writeBool(value != null);
if (value != null) {
writeConstant(value);
}
}
@override
void writeConstants(Iterable<ConstantValue> values, {bool allowNull: false}) {
if (values == null) {

View file

@ -15,6 +15,7 @@ import '../elements/entities.dart';
import '../elements/indexed.dart';
import '../elements/types.dart';
import '../ir/static_type_base.dart';
import '../js_backend/constant_system_javascript.dart';
import '../js_model/closure.dart';
import '../js_model/locals.dart';
@ -315,6 +316,9 @@ abstract class DataSink {
/// Writes the constant [value] to this data sink.
void writeConstant(ConstantValue value);
/// Writes the potentially `null` constant [value] to this data sink.
void writeConstantOrNull(ConstantValue value);
/// Writes constant [values] to this data sink. If [allowNull] is `true`,
/// [values] is allowed to be `null`.
///
@ -626,6 +630,9 @@ abstract class DataSource {
/// Reads a constant value from this data source.
ConstantValue readConstant();
/// Reads a potentially `null` constant value from this data source.
ConstantValue readConstantOrNull();
/// Reads a double value from this data source.
double readDoubleValue();

View file

@ -554,7 +554,8 @@ class KernelSsaGraphBuilder extends ir.Visitor
// Null guard ensures an error if we are being called from an explicit
// 'new' of the constructor instead of via an upgrade. It is optimized out
// if there are field initializers.
add(new HFieldGet(null, newObject, abstractValueDomain.dynamicType,
add(new HFieldGet(
null, newObject, abstractValueDomain.dynamicType, sourceInformation,
isAssignable: false));
for (int i = 0; i < fields.length; i++) {
add(new HFieldSet(abstractValueDomain, fields[i], newObject,
@ -802,10 +803,11 @@ class KernelSsaGraphBuilder extends ir.Visitor
var foundSuperOrRedirectCall = false;
for (var initializer in constructor.initializers) {
if (initializer is ir.FieldInitializer) {
// TODO(sra): Skip fields initialized in allocator.
initializer.value.accept(this);
constructorData.fieldValues[_elementMap.getField(initializer.field)] =
pop();
FieldEntity field = _elementMap.getField(initializer.field);
if (!closedWorld.fieldAnalysis.isInitializedInAllocator(field)) {
initializer.value.accept(this);
constructorData.fieldValues[field] = pop();
}
} else if (initializer is ir.SuperInitializer) {
assert(!foundSuperOrRedirectCall);
foundSuperOrRedirectCall = true;
@ -4761,6 +4763,11 @@ class KernelSsaGraphBuilder extends ir.Visitor
if (member == null) {
_generateSuperNoSuchMethod(node, _elementMap.getSelector(node).name,
const <HInstruction>[], const <DartType>[], sourceInformation);
} else if (member.isField &&
closedWorld.fieldAnalysis.isEffectivelyConstant(member)) {
ConstantValue value = closedWorld.fieldAnalysis.getConstantValue(member);
stack.add(graph.addConstant(value, closedWorld,
sourceInformation: sourceInformation));
} else {
_buildInvokeSuper(
_elementMap.getSelector(node),
@ -5331,10 +5338,9 @@ class KernelSsaGraphBuilder extends ir.Visitor
function is! ConstructorBodyEntity &&
(mask == null ||
abstractValueDomain.isNull(mask).isPotentiallyTrue)) {
add(new HFieldGet(
null, providedArguments[0], abstractValueDomain.dynamicType,
isAssignable: false)
..sourceInformation = sourceInformation);
add(new HFieldGet(null, providedArguments[0],
abstractValueDomain.dynamicType, sourceInformation,
isAssignable: false));
}
List<HInstruction> compiledArguments = _completeCallArgumentsList(
function, selector, providedArguments, currentNode);

View file

@ -346,9 +346,10 @@ class LocalsHandler {
AbstractValue type = local is BoxLocal
? _abstractValueDomain.nonNullType
: getTypeOfCapturedVariable(redirect);
HInstruction fieldGet = new HFieldGet(redirect, receiver, type);
HInstruction fieldGet =
new HFieldGet(redirect, receiver, type, sourceInformation);
builder.add(fieldGet);
return fieldGet..sourceInformation = sourceInformation;
return fieldGet;
} else if (isBoxed(local)) {
FieldEntity redirect = redirectionMapping[local];
BoxLocal localBox;
@ -363,10 +364,10 @@ class LocalsHandler {
assert(localBox != null);
HInstruction box = readLocal(localBox);
HInstruction lookup =
new HFieldGet(redirect, box, getTypeOfCapturedVariable(redirect));
HInstruction lookup = new HFieldGet(redirect, box,
getTypeOfCapturedVariable(redirect), sourceInformation);
builder.add(lookup);
return lookup..sourceInformation = sourceInformation;
return lookup;
} else {
assert(_isUsedInTryOrGenerator(local));
HLocalValue localValue = getLocal(local);

View file

@ -2,6 +2,7 @@
// 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.
import '../elements/entities.dart';
import '../util/features.dart';
import 'nodes.dart';
@ -28,8 +29,12 @@ class OptimizationTestLog {
void registerFieldGet(HInvokeDynamicGetter original, HFieldGet converted) {
Features features = new Features();
features['name'] =
'${converted.element.enclosingClass.name}.${converted.element.name}';
if (converted.element != null) {
features['name'] =
'${converted.element.enclosingClass.name}.${converted.element.name}';
} else {
features['name'] = '<null-guard>';
}
entries.add(new OptimizationLogEntry('FieldGet', features));
}
@ -44,6 +49,33 @@ class OptimizationTestLog {
entries.add(new OptimizationLogEntry('FieldSet', features));
}
void registerFieldCall(HInvokeDynamicMethod original, HFieldGet converted) {
Features features = new Features();
if (converted.element != null) {
features['name'] =
'${converted.element.enclosingClass.name}.${converted.element.name}';
} else {
features['name'] = '<null-guard>';
}
entries.add(new OptimizationLogEntry('FieldCall', features));
}
void registerConstantFieldGet(
HInvokeDynamicGetter original, FieldEntity field, HConstant converted) {
Features features = new Features();
features['name'] = '${field.enclosingClass.name}.${field.name}';
features['value'] = converted.constant.toStructuredText();
entries.add(new OptimizationLogEntry('ConstantFieldGet', features));
}
void registerConstantFieldCall(
HInvokeDynamicMethod original, FieldEntity field, HConstant converted) {
Features features = new Features();
features['name'] = '${field.enclosingClass.name}.${field.name}';
features['value'] = converted.constant.toStructuredText();
entries.add(new OptimizationLogEntry('ConstantFieldCall', features));
}
Features _registerSpecializer(
HInvokeDynamic original, HInstruction converted, String name,
[String unconvertedName]) {

View file

@ -1866,10 +1866,12 @@ class HFieldGet extends HFieldAccess {
final bool isAssignable;
HFieldGet(FieldEntity element, HInstruction receiver, AbstractValue type,
SourceInformation sourceInformation,
{bool isAssignable})
: this.isAssignable =
(isAssignable != null) ? isAssignable : element.isAssignable,
super(element, <HInstruction>[receiver], type) {
this.sourceInformation = sourceInformation;
sideEffects.clearAllSideEffects();
sideEffects.clearAllDependencies();
setUseGvn();

View file

@ -696,8 +696,8 @@ class SsaInstructionSimplifier extends HBaseVisitor
if (folded != node) return folded;
}
AbstractValue receiverType =
node.getDartReceiver(_closedWorld).instructionType;
HInstruction receiver = node.getDartReceiver(_closedWorld);
AbstractValue receiverType = receiver.instructionType;
MemberEntity element =
_closedWorld.locateSingleMember(node.selector, receiverType);
// TODO(ngeoffray): Also fold if it's a getter or variable.
@ -734,20 +734,46 @@ class SsaInstructionSimplifier extends HBaseVisitor
FieldEntity field = element;
if (!_nativeData.isNativeMember(field) &&
!node.isCallOnInterceptor(_closedWorld)) {
HInstruction receiver = node.getDartReceiver(_closedWorld);
AbstractValue type = AbstractValueFactory.inferredTypeForMember(
// ignore: UNNECESSARY_CAST
field as Entity,
_globalInferenceResults);
HInstruction load = new HFieldGet(field, receiver, type);
node.block.addBefore(node, load);
// Insertion point for the closure call.
HInstruction insertionPoint = node;
HInstruction load;
if (_closedWorld.fieldAnalysis.isEffectivelyConstant(field)) {
// The field is elided and replace it with its constant value.
if (_abstractValueDomain.isNull(receiverType).isPotentiallyTrue) {
// The receiver is potentially `null` so we insert a null receiver
// guard to trigger a null pointer exception.
//
// This could be inserted unconditionally and removed by later
// optimizations if unnecessary, but performance we do it
// conditionally here.
// TODO(35996): Replace with null receiver guard instruction.
HInstruction dummyGet = new HFieldGet(null, receiver,
_abstractValueDomain.dynamicType, node.sourceInformation,
isAssignable: false);
_log?.registerFieldCall(node, dummyGet);
node.block.addBefore(node, dummyGet);
insertionPoint = dummyGet;
}
ConstantValue value =
_closedWorld.fieldAnalysis.getConstantValue(field);
load = _graph.addConstant(value, _closedWorld,
sourceInformation: node.sourceInformation);
_log?.registerConstantFieldCall(node, field, load);
} else {
AbstractValue type = AbstractValueFactory.inferredTypeForMember(
field, _globalInferenceResults);
load = new HFieldGet(field, receiver, type, node.sourceInformation);
_log?.registerFieldCall(node, load);
node.block.addBefore(node, load);
insertionPoint = load;
}
Selector callSelector = new Selector.callClosureFrom(node.selector);
List<HInstruction> inputs = <HInstruction>[load]
..addAll(node.inputs.skip(node.isInterceptedCall ? 2 : 1));
HInstruction closureCall = new HInvokeClosure(
callSelector, inputs, node.instructionType, node.typeArguments)
..sourceInformation = node.sourceInformation;
node.block.addAfter(load, closureCall);
node.block.addAfter(insertionPoint, closureCall);
return closureCall;
}
}
@ -1215,17 +1241,42 @@ class SsaInstructionSimplifier extends HBaseVisitor
if (folded != node) return folded;
}
HInstruction receiver = node.getDartReceiver(_closedWorld);
AbstractValue receiverType = receiver.instructionType;
FieldEntity field = node.element is FieldEntity
? node.element
: findConcreteFieldForDynamicAccess(node, receiver);
if (field != null) {
HFieldGet result = _directFieldGet(receiver, field, node);
_log?.registerFieldGet(node, result);
return result;
if (_closedWorld.fieldAnalysis.isEffectivelyConstant(field)) {
// The field is elided and replace it with its constant value.
if (_abstractValueDomain.isNull(receiverType).isPotentiallyTrue) {
// The receiver is potentially `null` so we insert a null receiver
// guard to trigger a null pointer exception.
//
// This could be inserted unconditionally and removed by later
// optimizations if unnecessary, but performance we do it
// conditionally here.
// TODO(35996): Replace with null receiver guard instruction.
HInstruction dummyGet = new HFieldGet(null, receiver,
_abstractValueDomain.dynamicType, node.sourceInformation,
isAssignable: false);
_log?.registerFieldGet(node, dummyGet);
node.block.addBefore(node, dummyGet);
}
ConstantValue constant =
_closedWorld.fieldAnalysis.getConstantValue(field);
HConstant result = _graph.addConstant(constant, _closedWorld,
sourceInformation: node.sourceInformation);
_log?.registerConstantFieldGet(node, field, result);
return result;
} else {
HFieldGet result = _directFieldGet(receiver, field, node);
_log?.registerFieldGet(node, result);
return result;
}
}
node.element ??= _closedWorld.locateSingleMember(
node.selector, receiver.instructionType);
node.element ??=
_closedWorld.locateSingleMember(node.selector, receiverType);
if (node.element != null &&
node.element.name == node.selector.name &&
node.element.isFunction) {
@ -1256,8 +1307,8 @@ class SsaInstructionSimplifier extends HBaseVisitor
field, _globalInferenceResults);
}
return new HFieldGet(field, receiver, type, isAssignable: isAssignable)
..sourceInformation = node.sourceInformation;
return new HFieldGet(field, receiver, type, node.sourceInformation,
isAssignable: isAssignable);
}
HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) {

View file

@ -65,6 +65,7 @@ main() {
// Check that a gvn'able instruction in the loop header gets hoisted.
const String TEST_SIX = r"""
class A {
@pragma('dart2js:noElision')
final field = 54;
}

View file

@ -16,6 +16,7 @@ const String TEST_ONE = r"""
const String TEST_TWO = r"""
class A {
@pragma('dart2js:noElision')
var length;
}
foo(a) {
@ -37,11 +38,13 @@ main() {
// intercepted, is turned into a regular getter call or field
// access.
await compile(TEST_TWO, entry: 'foo', check: (String generated) {
Expect.isFalse(generated.contains(r'a.get$length()'));
Expect.isFalse(generated.contains(r'a.get$length()'),
'a.get\$length() not expected in\n$generated');
Expect.isTrue(generated.contains(new RegExp(r'[$A-Z]+\.A\$\(\)\.length')),
'.length expected in\n$generated');
Expect.isTrue(
generated.contains(new RegExp(r'[$A-Z]+\.A\$\(\)\.length')));
Expect.isTrue(
generated.contains(new RegExp(r'[$A-Z]+\.get\$length\$as\(a\)')));
generated.contains(new RegExp(r'[$A-Z]+\.get\$length\$as\(a\)')),
'*.get\$length expected in\n$generated');
});
}

View file

@ -14,10 +14,11 @@ main() {
class Class1a {
/*element: Class1a.field1:emitted*/
@pragma('dart2js:noElision')
int field1;
}
/*element: method1:params=1*/
/*element: method1:access=[field1],params=1*/
@pragma('dart2js:noInline')
method1(dynamic c) {
return c.field1;
@ -25,10 +26,11 @@ method1(dynamic c) {
class Class2a<T> {
/*element: Class2a.field2:emitted*/
@pragma('dart2js:noElision')
T field2;
}
/*element: method2:params=1*/
/*element: method2:access=[field2],params=1*/
@pragma('dart2js:noInline')
method2(dynamic c) {
return c.field2;
@ -36,11 +38,13 @@ method2(dynamic c) {
class Class3a {
/*element: Class3a.field3:emitted,get=simple*/
@pragma('dart2js:noElision')
int field3;
}
class Class3b {
/*element: Class3b.field3:emitted,get=simple*/
@pragma('dart2js:noElision')
int field3;
}
@ -52,11 +56,13 @@ method3(dynamic c) {
class Class4a {
/*element: Class4a.field4:emitted,get=simple*/
@pragma('dart2js:noElision')
int field4;
}
class Class4b implements Class4a {
/*element: Class4b.field4:emitted,get=simple*/
@pragma('dart2js:noElision')
int field4;
}

View file

@ -0,0 +1,88 @@
// 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.
import 'package:expect/expect.dart';
/*element: _field4:params=0*/
_field4() => 4;
class Class1 {
/*element: Class1.field1:elided*/
var field1 = 0;
/*element: Class1.field2:emitted*/
@pragma('dart2js:noElision')
var field2 = 1;
/*element: Class1.field3:elided,get=simple*/
var field3 = 2;
/*element: Class1.field4:elided*/
var field4 = _field4;
}
/*element: method1:params=1*/
@pragma('dart2js:noInline')
method1(Class1 c) {
return c.field1;
}
/*element: method2:access=[field2],params=1*/
@pragma('dart2js:noInline')
method2(Class1 c) {
return c.field2;
}
class Class2 {
/*element: Class2.field3:elided,get=simple*/
final field3 = 3;
}
/*element: method3:calls=[get$field3(0)],params=1*/
@pragma('dart2js:noInline')
method3(c) {
return c.field3;
}
class Class3 extends Class1 {
/*element: Class3.method4:params=0*/
@pragma('dart2js:noInline')
method4() {
return super.field1;
}
}
class Class4 extends Class1 {
/*element: Class4.method5:calls=[_field4(0)],params=0*/
@pragma('dart2js:noInline')
method5() {
return super.field4();
}
}
/*element: method6:access=[toString],params=1*/
@pragma('dart2js:noInline')
method6(Class1 c) {
return c.field1;
}
/*element: method7:access=[toString],calls=[_field4(0)],params=1*/
@pragma('dart2js:noInline')
method7(Class1 c) {
return c.field4();
}
/*element: main:calls=*,params=0*/
main() {
Expect.equals(0, method1(new Class1()));
Expect.equals(1, method2(new Class1()));
Expect.equals(2, method3(new Class1()));
Expect.equals(3, method3(new Class2()));
Expect.equals(0, new Class3().method4());
Expect.equals(4, new Class4().method5());
Expect.equals(0, method6(new Class1()));
Expect.throws(/*calls=[method6(1)],params=0*/ () => method6(null));
Expect.equals(4, method7(new Class1()));
Expect.throws(/*calls=[method7(1)],params=0*/ () => method7(null));
}

View file

@ -2,7 +2,7 @@
// 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.
/*element: main:calls=*,params=0*/
/*element: main:access=*,calls=*,params=0*/
main() {
method1(new Class1a());
method2(new Class2a());

View file

@ -32,6 +32,7 @@ set field2c(_) {}
class Class {
/*element: Class.field1a:emitted*/
@pragma('dart2js:noElision')
var field1a;
/*element: Class.field1b:elided*/

View file

@ -5,7 +5,7 @@
// ignore: import_internal_library
import 'dart:_js_helper';
/*element: Class.:params=1*/
/*element: Class.:access=[toString],params=1*/
@Native('Class')
class Class {
/*element: Class.method1:calls=[method1()],params=1*/

View file

@ -63,6 +63,7 @@ class Tags {
static const String isElided = 'elided';
static const String assignment = 'assign';
static const String isLazy = 'lazy';
static const String propertyAccess = 'access';
}
/// AST visitor for computing inference data for a member.
@ -133,6 +134,8 @@ class ModelIrComputer extends IrDataExtractor<Features> {
features[Tags.parameterCount] = '${code.params.length}';
}
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;
@ -159,6 +162,7 @@ class ModelIrComputer extends IrDataExtractor<Features> {
features.addElement(
tag, '${prefix}${name}(${node.arguments.length})');
}
handledAccesses.add(target);
}
}
});
@ -170,6 +174,7 @@ class ModelIrComputer extends IrDataExtractor<Features> {
registerCalls(Tags.parameterStub, stub.code, '${stub.name.key}:');
}
}
forEachNode(code, onAssignment: (js.Assignment node) {
js.Expression leftHandSide = node.leftHandSide;
if (leftHandSide is js.PropertyAccess) {
@ -182,9 +187,41 @@ class ModelIrComputer extends IrDataExtractor<Features> {
}
if (name != null) {
features.addElement(Tags.assignment, '${name}');
handledAccesses.add(leftHandSide);
}
}
});
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()) {
// 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}');
}
});
return features;
}
}

View file

@ -3,8 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
main() {
new Class1.a();
var c = new Class1.a();
c.field4a = null;
new Class1.b();
print(c.field1);
print(c.field2);
print(c.field3);
print(c.field4a);
print(c.field4b);
print(c.field5);
}
class Class1 {
@ -12,8 +20,11 @@ class Class1 {
var field2;
var field3;
/*element: Class1.field4:initial=IntConstant(4)*/
var field4 = 4;
/*element: Class1.field4a:initial=IntConstant(4)*/
var field4a = 4;
/*element: Class1.field4b:constant=IntConstant(4)*/
var field4b = 4;
var field5 = 5;

View file

@ -3,108 +3,336 @@
// BSD-style license that can be found in the LICENSE file.
main() {
new Class1();
new Class2();
use1(new Class1());
use2(new Class2());
}
@pragma('dart2js:noInline')
use(Object o) {
print(o);
}
@pragma('dart2js:noInline')
use1(Class1 c1) {
c1.field0a = null;
c1.field1a = null;
c1.field2a = null;
c1.field3a = null;
c1.field4a = null;
c1.field5a = null;
c1.field6a = null;
c1.field7a = null;
c1.field8a = null;
c1.field9a = null;
c1.field9c = null;
c1.field10a = null;
c1.field10c = null;
c1.field11a = null;
c1.field12a = null;
c1.field13a = null;
use(c1.field0a);
use(c1.field0b);
use(c1.field1a);
use(c1.field1b);
use(c1.field2a);
use(c1.field2b);
use(c1.field3a);
use(c1.field3b);
use(c1.field4a);
use(c1.field4b);
use(c1.field5a);
use(c1.field5b);
use(c1.field6a);
use(c1.field6b);
use(c1.field7a);
use(c1.field7b);
use(c1.field8a);
use(c1.field8b);
use(c1.field9a);
use(c1.field9b);
use(c1.field9c);
use(c1.field9d);
use(c1.field10a);
use(c1.field10b);
use(c1.field10c);
use(c1.field10d);
use(c1.field11a);
use(c1.field11b);
use(c1.field12a);
use(c1.field12b);
use(c1.field13a);
use(c1.field13b);
}
@pragma('dart2js:noInline')
use2(Class2 c2) {
c2.field1a = null;
c2.field2a = null;
c2.field3a = null;
c2.field4a = null;
c2.field5a = null;
c2.field6a = null;
c2.field7a = null;
c2.field8a = null;
c2.field9a = null;
c2.field9c = null;
c2.field10a = null;
c2.field10c = null;
c2.field11a = null;
c2.field12a = null;
c2.field13a = null;
use(c2.field1a);
use(c2.field1b);
use(c2.field2a);
use(c2.field2b);
use(c2.field3a);
use(c2.field3b);
use(c2.field4a);
use(c2.field4b);
use(c2.field5a);
use(c2.field5b);
use(c2.field6a);
use(c2.field6b);
use(c2.field7a);
use(c2.field7b);
use(c2.field8a);
use(c2.field8b);
use(c2.field9a);
use(c2.field9b);
use(c2.field9c);
use(c2.field9d);
use(c2.field10a);
use(c2.field10b);
use(c2.field10c);
use(c2.field10d);
use(c2.field11a);
use(c2.field11b);
use(c2.field12a);
use(c2.field12b);
use(c2.field13a);
use(c2.field13b);
}
const bool const1 = true;
class Class1 {
/*element: Class1.field0:initial=NullConstant*/
var field0;
/*element: Class1.field0a:initial=NullConstant*/
var field0a;
/*element: Class1.field1:initial=NullConstant*/
var field1 = null;
/*element: Class1.field0b:constant=NullConstant*/
var field0b;
/*element: Class1.field2:initial=BoolConstant(true)*/
var field2 = true;
/*element: Class1.field1a:initial=NullConstant*/
var field1a = null;
/*element: Class1.field3:initial=BoolConstant(false)*/
var field3 = false;
/*element: Class1.field1b:constant=NullConstant*/
var field1b = null;
/*element: Class1.field4:initial=IntConstant(0)*/
var field4 = 0;
/*element: Class1.field2a:initial=BoolConstant(true)*/
var field2a = true;
/*element: Class1.field5:initial=IntConstant(1)*/
var field5 = 1;
/*element: Class1.field2b:constant=BoolConstant(true)*/
var field2b = true;
/*element: Class1.field6:initial=StringConstant("")*/
var field6 = '';
/*element: Class1.field3a:initial=BoolConstant(false)*/
var field3a = false;
/*element: Class1.field7:initial=StringConstant("foo")*/
var field7 = 'foo';
/*element: Class1.field3b:constant=BoolConstant(false)*/
var field3b = false;
/*element: Class1.field8:*/
var field8 = 0.5;
/*element: Class1.field4a:initial=IntConstant(0)*/
var field4a = 0;
/*element: Class1.field9:*/
var field9 = const [];
/*element: Class1.field4b:constant=IntConstant(0)*/
var field4b = 0;
/*element: Class1.field10:*/
var field10 = const {};
/*element: Class1.field5a:initial=IntConstant(1)*/
var field5a = 1;
/*element: Class1.field11:*/
var field11 = #foo;
/*element: Class1.field5b:constant=IntConstant(1)*/
var field5b = 1;
/*element: Class1.field12:initial=IntConstant(5)*/
var field12 = 2 + 3;
/*element: Class1.field6a:initial=StringConstant("")*/
var field6a = '';
/*element: Class1.field13:initial=BoolConstant(true)*/
var field13 = const1;
/*element: Class1.field6b:constant=StringConstant("")*/
var field6b = '';
/*element: Class1.field7a:initial=StringConstant("foo")*/
var field7a = 'foo';
/*element: Class1.field7b:constant=StringConstant("foo")*/
var field7b = 'foo';
/*element: Class1.field8a:*/
var field8a = 0.5;
/*element: Class1.field8b:constant=DoubleConstant(0.5)*/
var field8b = 0.5;
/*element: Class1.field9a:*/
var field9a = const [];
/*element: Class1.field9b:constant=ListConstant([])*/
var field9b = const [];
/*element: Class1.field9c:*/
var field9c = const [0, 1];
/*element: Class1.field9d:constant=ListConstant(<int>[IntConstant(0), IntConstant(1), IntConstant(2)])*/
var field9d = const [0, 1, 2];
/*element: Class1.field10a:*/
var field10a = const {};
/*element: Class1.field10b:constant=MapConstant({})*/
var field10b = const {};
/*element: Class1.field10c:*/
var field10c = const {0: 1, 2: 3};
/*element: Class1.field10d:constant=MapConstant(<int, int>{IntConstant(0): IntConstant(1), IntConstant(2): IntConstant(3), IntConstant(4): IntConstant(5)})*/
var field10d = const {0: 1, 2: 3, 4: 5};
/*element: Class1.field11a:*/
var field11a = #foo;
/*element: Class1.field11b:constant=ConstructedConstant(Symbol(_name=StringConstant("foo")))*/
var field11b = #foo;
/*element: Class1.field12a:initial=IntConstant(5)*/
var field12a = 2 + 3;
/*element: Class1.field12b:constant=IntConstant(5)*/
var field12b = 2 + 3;
/*element: Class1.field13a:initial=BoolConstant(true)*/
var field13a = const1;
/*element: Class1.field13b:constant=BoolConstant(true)*/
var field13b = const1;
}
class Class2 {
/*element: Class2.field1:*/
var field1;
/*element: Class2.field1a:*/
var field1a;
/*element: Class2.field2:*/
var field2;
/*element: Class2.field1b:*/
var field1b;
/*element: Class2.field3:*/
var field3;
/*element: Class2.field2a:*/
var field2a;
/*element: Class2.field4:*/
var field4;
/*element: Class2.field2b:*/
var field2b;
/*element: Class2.field5:*/
var field5;
/*element: Class2.field3a:*/
var field3a;
/*element: Class2.field6:*/
var field6;
/*element: Class2.field3b:*/
var field3b;
/*element: Class2.field7:*/
var field7;
/*element: Class2.field4a:*/
var field4a;
/*element: Class2.field8:*/
var field8;
/*element: Class2.field4b:*/
var field4b;
/*element: Class2.field9:*/
var field9;
/*element: Class2.field5a:*/
var field5a;
/*element: Class2.field10:*/
var field10;
/*element: Class2.field5b:*/
var field5b;
/*element: Class2.field11:*/
var field11;
/*element: Class2.field6a:*/
var field6a;
/*element: Class2.field12:*/
var field12;
/*element: Class2.field6b:*/
var field6b;
/*element: Class2.field13:*/
var field13;
/*element: Class2.field7a:*/
var field7a;
/*element: Class2.field7b:*/
var field7b;
/*element: Class2.field8a:*/
var field8a;
/*element: Class2.field8b:*/
var field8b;
/*element: Class2.field9a:*/
var field9a;
/*element: Class2.field9b:*/
var field9b;
/*element: Class2.field9c:*/
var field9c;
/*element: Class2.field9d:*/
var field9d;
/*element: Class2.field10a:*/
var field10a;
/*element: Class2.field10b:*/
var field10b;
/*element: Class2.field10c:*/
var field10c;
/*element: Class2.field10d:*/
var field10d;
/*element: Class2.field11a:*/
var field11a;
/*element: Class2.field11b:*/
var field11b;
/*element: Class2.field12a:*/
var field12a;
/*element: Class2.field12b:*/
var field12b;
/*element: Class2.field13a:*/
var field13a;
/*element: Class2.field13b:*/
var field13b;
Class2()
: field1 = null,
field2 = true,
field3 = false,
field4 = 0,
field5 = 1,
field6 = '',
field7 = 'foo',
field8 = 0.5,
field9 = const [],
field10 = const {},
field11 = #foo,
field12 = 2 + 3,
field13 = const1;
: field1a = null,
field1b = null,
field2a = true,
field2b = true,
field3a = false,
field3b = false,
field4a = 0,
field4b = 0,
field5a = 1,
field5b = 1,
field6a = '',
field6b = '',
field7a = 'foo',
field7b = 'foo',
field8a = 0.5,
field8b = 0.5,
field9a = const [],
field9b = const [],
field9c = const [0, 1],
field9d = const [0, 1, 2],
field10a = const {},
field10b = const {},
field10c = const {0: 1, 2: 3},
field10d = const {0: 1, 2: 3, 4: 5},
field11a = #foo,
field11b = #foo,
field12a = 2 + 3,
field12b = 2 + 3,
field13a = const1,
field13b = const1;
}

View file

@ -5,7 +5,6 @@
import 'dart:io';
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/constants/values.dart';
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/ir/util.dart';
import 'package:compiler/src/js_backend/field_analysis.dart';
@ -25,6 +24,7 @@ main(List<String> args) {
class Tags {
static const String initialValue = 'initial';
static const String constantValue = 'constant';
}
class JAllocatorAnalysisDataComputer extends DataComputer<Features> {
@ -38,10 +38,14 @@ class JAllocatorAnalysisDataComputer extends DataComputer<Features> {
JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
JFieldAnalysis allocatorAnalysis = closedWorld.fieldAnalysis;
ir.Member node = closedWorld.elementMap.getMemberDefinition(member).node;
ConstantValue initialValue = allocatorAnalysis.initializerValue(member);
Features features = new Features();
if (initialValue != null) {
features[Tags.initialValue] = initialValue.toStructuredText();
if (allocatorAnalysis.isInitializedInAllocator(member)) {
features[Tags.initialValue] =
allocatorAnalysis.initializerValue(member).toStructuredText();
}
if (allocatorAnalysis.isEffectivelyConstant(member)) {
features[Tags.constantValue] =
allocatorAnalysis.getConstantValue(member).toStructuredText();
}
Id id = computeEntityId(node);
actualMap[id] = new ActualData<Features>(

View file

@ -14,10 +14,10 @@ import 'package:compiler/src/constants/constructors.dart';
import 'package:compiler/src/constants/evaluation.dart';
import 'package:compiler/src/constants/expressions.dart';
import 'package:compiler/src/constants/values.dart';
import 'package:compiler/src/constant_system_dart.dart';
import 'package:compiler/src/diagnostics/messages.dart';
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/elements/types.dart';
import 'package:compiler/src/js_backend/constant_system_javascript.dart';
import 'package:compiler/src/kernel/kernel_strategy.dart';
import 'package:compiler/src/kernel/element_map_impl.dart';
import '../helpers/memory_compiler.dart';
@ -146,7 +146,8 @@ const List<TestData> DATA = const [
const ConstantData('false', 'BoolConstant(false)'),
const ConstantData('true', 'BoolConstant(true)'),
const ConstantData('0', 'IntConstant(0)'),
const ConstantData('0.0', 'DoubleConstant(0.0)'),
const ConstantData('0.0', 'IntConstant(0)'),
const ConstantData('0.5', 'DoubleConstant(0.5)'),
const ConstantData('"foo"', 'StringConstant("foo")'),
const ConstantData('1 + 2', 'IntConstant(3)'),
const ConstantData('-(1)', 'IntConstant(-1)'),
@ -633,7 +634,7 @@ Future testData(TestData data) async {
MemoryEnvironment environment =
new MemoryEnvironment(getEnvironment(compiler, field), env);
ConstantValue value =
constant.evaluate(environment, DART_CONSTANT_SYSTEM);
constant.evaluate(environment, JavaScriptConstantSystem.only);
Expect.isNotNull(
value,

View file

@ -0,0 +1,83 @@
// 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.
import 'package:expect/expect.dart';
class Class1 {
var field1 = 0;
@pragma('dart2js:noElision')
var field2 = 0;
var field3 = 0;
}
/*element: method1:
ConstantFieldGet=[name=Class1.field1&value=IntConstant(0)],
FieldGet=[]
*/
@pragma('dart2js:noInline')
method1(Class1 c) {
return c.field1;
}
/*element: method2:FieldGet=[name=Class1.field2]*/
@pragma('dart2js:noInline')
method2(Class1 c) {
return c.field2;
}
class Class2 {
var field3 = 0;
}
@pragma('dart2js:noInline')
method3(c) {
return c.field3;
}
int _field4() => 0;
class Class3 {
int Function() field4 = _field4;
}
/*element: method4:
ConstantFieldCall=[name=Class3.field4&value=FunctionConstant(_field4)],
FieldCall=[]
*/
@pragma('dart2js:noInline')
method4(Class3 c) {
return c.field4();
}
/*element: method6:
ConstantFieldGet=[name=Class1.field1&value=IntConstant(0)],
FieldGet=[name=<null-guard>]
*/
@pragma('dart2js:noInline')
method6(Class1 c) {
return c.field1;
}
/*element: method7:
ConstantFieldCall=[name=Class3.field4&value=FunctionConstant(_field4)],
FieldCall=[name=<null-guard>]
*/
@pragma('dart2js:noInline')
method7(Class3 c) {
return c.field4();
}
main() {
Expect.equals(0, method1(new Class1()));
Expect.equals(0, method2(new Class1()));
Expect.equals(0, method3(new Class1()));
Expect.equals(0, method3(new Class2()));
Expect.equals(0, method4(new Class3()));
Expect.equals(0, method6(new Class1()));
Expect.throws(() => method6(null));
Expect.equals(4, method7(new Class3()));
Expect.throws(() => method7(null));
}

View file

@ -10,9 +10,11 @@ main() {
method3(new Class3b());
method4(new Class4a());
method4(new Class4b());
method5(new Class5a());
}
class Class1a {
@pragma('dart2js:noElision')
int field1;
}
@ -23,6 +25,7 @@ method1(Class1a c) {
}
class Class2a {
@pragma('dart2js:noElision')
int field2;
}
@ -60,3 +63,14 @@ class Class4b implements Class4a {
method4(Class4a c) {
return c.field4;
}
class Class5a {
@pragma('dart2js:noElision')
int Function() field5;
}
/*element: method5:FieldCall=[name=Class5a.field5]*/
@pragma('dart2js:noInline')
method5(Class5a c) {
return c.field5();
}

View file

@ -14,5 +14,6 @@ test(c) {
}
class Class {
@pragma('dart2js:noElision')
var field;
}

View file

@ -0,0 +1,18 @@
// 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.
import 'package:expect/expect.dart';
main() {
/*1:main*/ test(new Class());
}
@NoInline()
test(c) {
c.field. /*2:test*/ method();
}
class Class {
var field;
}

View file

@ -9,7 +9,8 @@ main() {
/*1:main*/ test();
}
// TODO(34942): Step 2 should point to the body block.
@NoInline()
test() async /*2:test*/ {
test /*2:test*/ () async {
/*4:test*/ throw '>ExceptionMarker<';
}

View file

@ -9,13 +9,15 @@ main() {
/*1:main*/ test1();
}
// TODO(34942): Step 3 should point to the body block.
@NoInline()
test1() async /*3:test1*/ {
test1 /*3:test1*/ () async {
// This call is on the stack when the error is thrown.
await /*5:test1*/ test2();
}
// TODO(34942): Step 7 should point to the body block.
@NoInline()
test2() async /*7:test2*/ {
test2 /*7:test2*/ () async {
/*9:test2*/ throw '>ExceptionMarker<';
}

View file

@ -8,8 +8,9 @@ main() {
/*1:main*/ test1();
}
// TODO(34942): Step 2 should point to the body block.
@NoInline()
test1() async /*2:test1*/ {
test1 /*2:test1*/ () async {
/*9:test1*/ test2();
}

View file

@ -8,6 +8,7 @@ import 'dart:io';
import 'package:args/args.dart';
import 'package:async_helper/async_helper.dart';
import 'package:compiler/compiler_new.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/dart2js.dart' as entry;
import 'package:sourcemap_testing/src/stacktrace_helper.dart';
@ -83,6 +84,7 @@ Future runTest(Test test, String config,
'-o$output',
'--libraries-spec=sdk/lib/libraries.json',
'--packages=${Platform.packageConfig}',
Flags.testMode,
input,
]..addAll(options);
print("Compiling dart2js ${arguments.join(' ')}");

View file

@ -0,0 +1,74 @@
// 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.
import 'package:expect/expect.dart';
_field4() => 4;
class Class1 {
var field1 = 0;
@pragma('dart2js:noElision')
var field2 = 1;
var field3 = 2;
var field4 = _field4;
}
@pragma('dart2js:noInline')
method1(Class1 c) {
return c.field1;
}
@pragma('dart2js:noInline')
method2(Class1 c) {
return c.field2;
}
class Class2 {
var field3 = 3;
}
@pragma('dart2js:noInline')
method3(c) {
return c.field3;
}
class Class3 extends Class1 {
@pragma('dart2js:noInline')
method4() {
return super.field1;
}
}
class Class4 extends Class1 {
@pragma('dart2js:noInline')
method5() {
return super.field4();
}
}
@pragma('dart2js:noInline')
method6(Class1 c) {
return c.field1;
}
@pragma('dart2js:noInline')
method7(Class1 c) {
return c.field4();
}
main() {
Expect.equals(0, method1(new Class1()));
Expect.equals(1, method2(new Class1()));
Expect.equals(2, method3(new Class1()));
Expect.equals(3, method3(new Class2()));
Expect.equals(0, new Class3().method4());
Expect.equals(4, new Class4().method5());
Expect.equals(0, method6(new Class1()));
Expect.throws(() => method6(null));
Expect.equals(4, method7(new Class1()));
Expect.throws(() => method7(null));
}