mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 14:43:32 +00:00
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:
parent
db62dc5323
commit
60e4f194b0
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, _) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -32,6 +32,7 @@ set field2c(_) {}
|
|||
|
||||
class Class {
|
||||
/*element: Class.field1a:emitted*/
|
||||
@pragma('dart2js:noElision')
|
||||
var field1a;
|
||||
|
||||
/*element: Class.field1b:elided*/
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -14,5 +14,6 @@ test(c) {
|
|||
}
|
||||
|
||||
class Class {
|
||||
@pragma('dart2js:noElision')
|
||||
var field;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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<';
|
||||
}
|
||||
|
|
|
@ -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<';
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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(' ')}");
|
||||
|
|
|
@ -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));
|
||||
}
|
Loading…
Reference in a new issue