mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Revert "[vm/aot/tfa] Tree shake write-only fields"
This reverts commit ff34fd8110
.
Reason: crash on the internal app
Issue: https://github.com/flutter/flutter/issues/50745
Change-Id: Ifcfbb38fa04d27558f9e78ca6b2a8637693c7d76
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135794
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
989180af3e
commit
8ac7f6bce7
|
@ -191,17 +191,12 @@ class _DirectInvocation extends _Invocation {
|
|||
case CallKind.PropertyGet:
|
||||
assertx(args.values.length == firstParamIndex);
|
||||
assertx(args.names.isEmpty);
|
||||
fieldValue.isGetterUsed = true;
|
||||
return fieldValue.getValue(
|
||||
typeFlowAnalysis, field.isStatic ? null : args.values[0]);
|
||||
|
||||
case CallKind.PropertySet:
|
||||
case CallKind.SetFieldInConstructor:
|
||||
assertx(args.values.length == firstParamIndex + 1);
|
||||
assertx(args.names.isEmpty);
|
||||
if (selector.callKind == CallKind.PropertySet) {
|
||||
fieldValue.isSetterUsed = true;
|
||||
}
|
||||
final Type setterArg = args.values[firstParamIndex];
|
||||
fieldValue.setValue(
|
||||
setterArg, typeFlowAnalysis, field.isStatic ? null : args.receiver);
|
||||
|
@ -211,7 +206,6 @@ class _DirectInvocation extends _Invocation {
|
|||
// Call via field.
|
||||
// TODO(alexmarkov): support function types and use inferred type
|
||||
// to get more precise return type.
|
||||
fieldValue.isGetterUsed = true;
|
||||
final receiver = fieldValue.getValue(
|
||||
typeFlowAnalysis, field.isStatic ? null : args.values[0]);
|
||||
if (receiver != const EmptyType()) {
|
||||
|
@ -778,12 +772,6 @@ class _FieldValue extends _DependencyTracker {
|
|||
/// Flag indicating if field initializer was executed.
|
||||
bool isInitialized = false;
|
||||
|
||||
/// Flag indicating if field getter was executed.
|
||||
bool isGetterUsed = false;
|
||||
|
||||
/// Flag indicating if field setter was executed.
|
||||
bool isSetterUsed = false;
|
||||
|
||||
_FieldValue(this.field, this.typeGuardSummary, TypesBuilder typesBuilder)
|
||||
: staticType = typesBuilder.fromStaticType(field.type, true) {
|
||||
if (field.initializer == null && _isDefaultValueOfFieldObservable()) {
|
||||
|
@ -1403,26 +1391,6 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if analysis found that getter corresponding to the given
|
||||
/// [field] could be executed.
|
||||
bool isFieldGetterUsed(Field field) {
|
||||
final fieldValue = _fieldValues[field];
|
||||
if (fieldValue != null) {
|
||||
return fieldValue.isGetterUsed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if analysis found that setter corresponding to the given
|
||||
/// [field] could be executed.
|
||||
bool isFieldSetterUsed(Field field) {
|
||||
final fieldValue = _fieldValues[field];
|
||||
if (fieldValue != null) {
|
||||
return fieldValue.isSetterUsed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isClassAllocated(Class c) => hierarchyCache.allocatedClasses.contains(c);
|
||||
|
||||
Call callSite(TreeNode node) => summaryCollector.callSites[node];
|
||||
|
|
|
@ -16,8 +16,7 @@ enum CallKind {
|
|||
Method, // x.foo(..) or foo()
|
||||
PropertyGet, // ... x.foo ...
|
||||
PropertySet, // x.foo = ...
|
||||
FieldInitializer, // run initializer of a field
|
||||
SetFieldInConstructor, // foo = ... in initializer list in a constructor
|
||||
FieldInitializer,
|
||||
}
|
||||
|
||||
/// [Selector] encapsulates the way of calling (at the call site).
|
||||
|
@ -56,7 +55,6 @@ abstract class Selector {
|
|||
return member.getterType;
|
||||
case CallKind.PropertySet:
|
||||
case CallKind.FieldInitializer:
|
||||
case CallKind.SetFieldInConstructor:
|
||||
return const BottomType();
|
||||
}
|
||||
return null;
|
||||
|
@ -74,8 +72,7 @@ abstract class Selector {
|
|||
case CallKind.PropertySet:
|
||||
return (member is Field) || ((member is Procedure) && member.isSetter);
|
||||
case CallKind.FieldInitializer:
|
||||
case CallKind.SetFieldInConstructor:
|
||||
return member is Field;
|
||||
return (member is Field);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -87,7 +84,6 @@ abstract class Selector {
|
|||
case CallKind.PropertyGet:
|
||||
return 'get ';
|
||||
case CallKind.PropertySet:
|
||||
case CallKind.SetFieldInConstructor:
|
||||
return 'set ';
|
||||
case CallKind.FieldInitializer:
|
||||
return 'init ';
|
||||
|
|
|
@ -800,7 +800,6 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
break;
|
||||
|
||||
case CallKind.PropertySet:
|
||||
case CallKind.SetFieldInConstructor:
|
||||
args.add(new Type.nullableAny());
|
||||
break;
|
||||
|
||||
|
@ -2072,11 +2071,8 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
TypeExpr visitFieldInitializer(FieldInitializer node) {
|
||||
final value = _visit(node.value);
|
||||
final args = new Args<TypeExpr>([_receiver, value]);
|
||||
_makeCall(
|
||||
node,
|
||||
new DirectSelector(node.field,
|
||||
callKind: CallKind.SetFieldInConstructor),
|
||||
args);
|
||||
_makeCall(node,
|
||||
new DirectSelector(node.field, callKind: CallKind.PropertySet), args);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,15 +68,12 @@ Component transformComponent(
|
|||
|
||||
final transformsStopWatch = new Stopwatch()..start();
|
||||
|
||||
final treeShaker = new TreeShaker(component, typeFlowAnalysis);
|
||||
treeShaker.transformComponent(component);
|
||||
new TreeShaker(component, typeFlowAnalysis).transformComponent(component);
|
||||
|
||||
new TFADevirtualization(
|
||||
component, typeFlowAnalysis, hierarchy, treeShaker.fieldMorpher)
|
||||
new TFADevirtualization(component, typeFlowAnalysis, hierarchy)
|
||||
.visitComponent(component);
|
||||
|
||||
new AnnotateKernel(component, typeFlowAnalysis, treeShaker.fieldMorpher)
|
||||
.visitComponent(component);
|
||||
new AnnotateKernel(component, typeFlowAnalysis).visitComponent(component);
|
||||
|
||||
transformsStopWatch.stop();
|
||||
|
||||
|
@ -91,10 +88,9 @@ Component transformComponent(
|
|||
/// Devirtualization based on results of type flow analysis.
|
||||
class TFADevirtualization extends Devirtualization {
|
||||
final TypeFlowAnalysis _typeFlowAnalysis;
|
||||
final FieldMorpher fieldMorpher;
|
||||
|
||||
TFADevirtualization(Component component, this._typeFlowAnalysis,
|
||||
ClassHierarchy hierarchy, this.fieldMorpher)
|
||||
TFADevirtualization(
|
||||
Component component, this._typeFlowAnalysis, ClassHierarchy hierarchy)
|
||||
: super(_typeFlowAnalysis.environment.coreTypes, component, hierarchy);
|
||||
|
||||
@override
|
||||
|
@ -102,8 +98,7 @@ class TFADevirtualization extends Devirtualization {
|
|||
{bool setter = false}) {
|
||||
final callSite = _typeFlowAnalysis.callSite(node);
|
||||
if (callSite != null) {
|
||||
final Member singleTarget = fieldMorpher
|
||||
.getMorphedMember(callSite.monomorphicTarget, isSetter: setter);
|
||||
final Member singleTarget = callSite.monomorphicTarget;
|
||||
if (singleTarget != null) {
|
||||
return new DirectCallMetadata(
|
||||
singleTarget, callSite.isNullableReceiver);
|
||||
|
@ -116,7 +111,6 @@ class TFADevirtualization extends Devirtualization {
|
|||
/// Annotates kernel AST with metadata using results of type flow analysis.
|
||||
class AnnotateKernel extends RecursiveVisitor<Null> {
|
||||
final TypeFlowAnalysis _typeFlowAnalysis;
|
||||
final FieldMorpher fieldMorpher;
|
||||
final DirectCallMetadataRepository _directCallMetadataRepository;
|
||||
final InferredTypeMetadataRepository _inferredTypeMetadata;
|
||||
final UnreachableNodeMetadataRepository _unreachableNodeMetadata;
|
||||
|
@ -126,7 +120,7 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
|
|||
final Class _intClass;
|
||||
Constant _nullConstant;
|
||||
|
||||
AnnotateKernel(Component component, this._typeFlowAnalysis, this.fieldMorpher)
|
||||
AnnotateKernel(Component component, this._typeFlowAnalysis)
|
||||
: _directCallMetadataRepository =
|
||||
component.metadata[DirectCallMetadataRepository.repositoryTag],
|
||||
_inferredTypeMetadata = new InferredTypeMetadataRepository(),
|
||||
|
@ -309,8 +303,7 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
|
|||
|
||||
// TODO(alexmarkov): figure out how to pass receiver type.
|
||||
}
|
||||
} else if (!member.isAbstract &&
|
||||
!fieldMorpher.isExtraMemberWithReachableBody(member)) {
|
||||
} else if (!member.isAbstract) {
|
||||
_setUnreachable(member);
|
||||
}
|
||||
|
||||
|
@ -319,15 +312,14 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
|
|||
// interface target, and table dispatch calls need selector IDs for all
|
||||
// interface targets.
|
||||
if (member.isInstanceMember) {
|
||||
final original = fieldMorpher.getOriginalMember(member);
|
||||
final attrs = new ProcedureAttributesMetadata(
|
||||
methodOrSetterCalledDynamically:
|
||||
_typeFlowAnalysis.isCalledDynamically(original),
|
||||
_typeFlowAnalysis.isCalledDynamically(member),
|
||||
getterCalledDynamically:
|
||||
_typeFlowAnalysis.isGetterCalledDynamically(original),
|
||||
hasThisUses: _typeFlowAnalysis.isCalledViaThis(original),
|
||||
hasNonThisUses: _typeFlowAnalysis.isCalledNotViaThis(original),
|
||||
hasTearOffUses: _typeFlowAnalysis.isTearOffTaken(original),
|
||||
_typeFlowAnalysis.isGetterCalledDynamically(member),
|
||||
hasThisUses: _typeFlowAnalysis.isCalledViaThis(member),
|
||||
hasNonThisUses: _typeFlowAnalysis.isCalledNotViaThis(member),
|
||||
hasTearOffUses: _typeFlowAnalysis.isTearOffTaken(member),
|
||||
methodOrSetterSelectorId:
|
||||
_tableSelectorAssigner.methodOrSetterSelectorId(member),
|
||||
getterSelectorId: _tableSelectorAssigner.getterSelectorId(member));
|
||||
|
@ -456,14 +448,12 @@ class TreeShaker {
|
|||
final Set<Member> _usedMembers = new Set<Member>();
|
||||
final Set<Extension> _usedExtensions = new Set<Extension>();
|
||||
final Set<Typedef> _usedTypedefs = new Set<Typedef>();
|
||||
FieldMorpher fieldMorpher;
|
||||
_TreeShakerTypeVisitor typeVisitor;
|
||||
_TreeShakerConstantVisitor constantVisitor;
|
||||
_TreeShakerPass1 _pass1;
|
||||
_TreeShakerPass2 _pass2;
|
||||
|
||||
TreeShaker(Component component, this.typeFlowAnalysis) {
|
||||
fieldMorpher = new FieldMorpher(this);
|
||||
typeVisitor = new _TreeShakerTypeVisitor(this);
|
||||
constantVisitor = new _TreeShakerConstantVisitor(this, typeVisitor);
|
||||
_pass1 = new _TreeShakerPass1(this);
|
||||
|
@ -482,25 +472,13 @@ class TreeShaker {
|
|||
bool isClassAllocated(Class c) => typeFlowAnalysis.isClassAllocated(c);
|
||||
bool isMemberUsed(Member m) => _usedMembers.contains(m);
|
||||
bool isExtensionUsed(Extension e) => _usedExtensions.contains(e);
|
||||
bool isMemberBodyReachable(Member m) =>
|
||||
typeFlowAnalysis.isMemberUsed(m) ||
|
||||
fieldMorpher.isExtraMemberWithReachableBody(m);
|
||||
bool isMemberBodyReachable(Member m) => typeFlowAnalysis.isMemberUsed(m);
|
||||
bool isFieldInitializerReachable(Field f) =>
|
||||
typeFlowAnalysis.isFieldInitializerUsed(f);
|
||||
bool isFieldGetterReachable(Field f) => typeFlowAnalysis.isFieldGetterUsed(f);
|
||||
bool isFieldSetterReachable(Field f) => typeFlowAnalysis.isFieldSetterUsed(f);
|
||||
bool isMemberReferencedFromNativeCode(Member m) =>
|
||||
typeFlowAnalysis.nativeCodeOracle.isMemberReferencedFromNativeCode(m);
|
||||
bool isTypedefUsed(Typedef t) => _usedTypedefs.contains(t);
|
||||
|
||||
bool retainField(Field f) =>
|
||||
isFieldGetterReachable(f) ||
|
||||
(f.initializer != null &&
|
||||
isFieldInitializerReachable(f) &&
|
||||
mayHaveSideEffects(f.initializer)) ||
|
||||
(f.isLate && f.isFinal) ||
|
||||
isMemberReferencedFromNativeCode(f);
|
||||
|
||||
void addClassUsedInType(Class c) {
|
||||
if (_classesUsedInType.add(c)) {
|
||||
if (kPrintDebug) {
|
||||
|
@ -536,15 +514,9 @@ class TreeShaker {
|
|||
} else if (m is Procedure) {
|
||||
func = m.function;
|
||||
if (m.forwardingStubSuperTarget != null) {
|
||||
m.forwardingStubSuperTarget = fieldMorpher.adjustInstanceCallTarget(
|
||||
m.forwardingStubSuperTarget,
|
||||
isSetter: m.isSetter);
|
||||
addUsedMember(m.forwardingStubSuperTarget);
|
||||
}
|
||||
if (m.forwardingStubInterfaceTarget != null) {
|
||||
m.forwardingStubInterfaceTarget = fieldMorpher
|
||||
.adjustInstanceCallTarget(m.forwardingStubInterfaceTarget,
|
||||
isSetter: m.isSetter);
|
||||
addUsedMember(m.forwardingStubInterfaceTarget);
|
||||
}
|
||||
} else if (m is Constructor) {
|
||||
|
@ -597,86 +569,6 @@ class TreeShaker {
|
|||
}
|
||||
}
|
||||
|
||||
class FieldMorpher {
|
||||
final TreeShaker shaker;
|
||||
final Set<Member> _extraMembersWithReachableBody = <Member>{};
|
||||
final Map<Field, Member> _gettersForRemovedFields = <Field, Member>{};
|
||||
final Map<Field, Member> _settersForRemovedFields = <Field, Member>{};
|
||||
final Map<Member, Field> _removedFields = <Member, Field>{};
|
||||
|
||||
FieldMorpher(this.shaker);
|
||||
|
||||
Member _createAccessorForRemovedField(Field field, bool isSetter) {
|
||||
assertx(!field.isStatic);
|
||||
assertx(!shaker.retainField(field));
|
||||
Procedure accessor;
|
||||
if (isSetter) {
|
||||
final isAbstract = !shaker.isFieldSetterReachable(field);
|
||||
final parameter = new VariableDeclaration('value', type: field.type)
|
||||
..isCovariant = field.isCovariant
|
||||
..isGenericCovariantImpl = field.isGenericCovariantImpl
|
||||
..fileOffset = field.fileOffset;
|
||||
accessor = new Procedure(
|
||||
field.name,
|
||||
ProcedureKind.Setter,
|
||||
new FunctionNode(null,
|
||||
positionalParameters: [parameter], returnType: const VoidType())
|
||||
..fileOffset = field.fileOffset,
|
||||
isAbstract: isAbstract,
|
||||
fileUri: field.fileUri);
|
||||
if (!isAbstract) {
|
||||
_extraMembersWithReachableBody.add(accessor);
|
||||
}
|
||||
} else {
|
||||
accessor = new Procedure(field.name, ProcedureKind.Getter,
|
||||
new FunctionNode(null, returnType: field.type),
|
||||
isAbstract: true, fileUri: field.fileUri);
|
||||
}
|
||||
accessor.fileOffset = field.fileOffset;
|
||||
field.enclosingClass.addMember(accessor);
|
||||
_removedFields[accessor] = field;
|
||||
shaker.addUsedMember(accessor);
|
||||
return accessor;
|
||||
}
|
||||
|
||||
/// Return a replacement for an instance call target.
|
||||
/// If necessary, creates a getter or setter as a replacement if target is a
|
||||
/// field which is going to be removed by the tree shaker.
|
||||
/// This method is used during tree shaker pass 1.
|
||||
Member adjustInstanceCallTarget(Member target, {bool isSetter = false}) {
|
||||
if (target is Field && !shaker.retainField(target)) {
|
||||
final targets =
|
||||
isSetter ? _settersForRemovedFields : _gettersForRemovedFields;
|
||||
return targets[target] ??=
|
||||
_createAccessorForRemovedField(target, isSetter);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
bool isExtraMemberWithReachableBody(Member member) =>
|
||||
_extraMembersWithReachableBody.contains(member);
|
||||
|
||||
/// Return a member which replaced [target] in instance calls.
|
||||
/// This method can be used after tree shaking to discover replacement.
|
||||
Member getMorphedMember(Member target, {bool isSetter = false}) {
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
final targets =
|
||||
isSetter ? _settersForRemovedFields : _gettersForRemovedFields;
|
||||
return targets[target] ?? target;
|
||||
}
|
||||
|
||||
/// Return original member which was replaced by [target] in instance calls.
|
||||
/// This method can be used after tree shaking.
|
||||
Member getOriginalMember(Member target) {
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
return _removedFields[target] ?? target;
|
||||
}
|
||||
}
|
||||
|
||||
/// Visits Dart types and collects all classes and typedefs used in types.
|
||||
/// This visitor is used during pass 1 of tree shaking. It is a separate
|
||||
/// visitor because [Transformer] does not provide a way to traverse types.
|
||||
|
@ -727,10 +619,9 @@ class _TreeShakerTypeVisitor extends RecursiveVisitor<Null> {
|
|||
/// transforms unreachable calls into 'throw' expressions.
|
||||
class _TreeShakerPass1 extends Transformer {
|
||||
final TreeShaker shaker;
|
||||
final FieldMorpher fieldMorpher;
|
||||
Procedure _unsafeCast;
|
||||
|
||||
_TreeShakerPass1(this.shaker) : fieldMorpher = shaker.fieldMorpher;
|
||||
_TreeShakerPass1(this.shaker);
|
||||
|
||||
void transform(Component component) {
|
||||
component.transformChildren(this);
|
||||
|
@ -773,9 +664,7 @@ class _TreeShakerPass1 extends Transformer {
|
|||
'Attempt to execute code removed by Dart AOT compiler (TFA)'));
|
||||
}
|
||||
for (var arg in args.reversed) {
|
||||
if (mayHaveSideEffects(arg)) {
|
||||
node = new Let(new VariableDeclaration(null, initializer: arg), node);
|
||||
}
|
||||
node = new Let(new VariableDeclaration(null, initializer: arg), node);
|
||||
}
|
||||
Statistics.callsDropped++;
|
||||
return node;
|
||||
|
@ -852,7 +741,7 @@ class _TreeShakerPass1 extends Transformer {
|
|||
|
||||
@override
|
||||
TreeNode visitField(Field node) {
|
||||
if (shaker.retainField(node)) {
|
||||
if (shaker.isMemberBodyReachable(node)) {
|
||||
if (kPrintTrace) {
|
||||
tracePrint("Visiting $node");
|
||||
}
|
||||
|
@ -864,10 +753,12 @@ class _TreeShakerPass1 extends Transformer {
|
|||
node.initializer = _makeUnreachableCall([])..parent = node;
|
||||
}
|
||||
}
|
||||
} else if (shaker.isFieldSetterReachable(node) && !node.isStatic) {
|
||||
// Make sure setter is created to replace the field even if field is not
|
||||
// used as an instance call target.
|
||||
fieldMorpher.adjustInstanceCallTarget(node, isSetter: true);
|
||||
} else if (shaker.isMemberReferencedFromNativeCode(node)) {
|
||||
// Preserve members referenced from native code to satisfy lookups, even
|
||||
// if they are not reachable. An instance member could be added via
|
||||
// native code entry point but still unreachable if no instances of
|
||||
// its enclosing class are allocated.
|
||||
shaker.addUsedMember(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
@ -879,8 +770,6 @@ class _TreeShakerPass1 extends Transformer {
|
|||
return _makeUnreachableCall(
|
||||
_flattenArguments(node.arguments, receiver: node.receiver));
|
||||
} else {
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
if (node.interfaceTarget != null) {
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
}
|
||||
|
@ -894,8 +783,6 @@ class _TreeShakerPass1 extends Transformer {
|
|||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver]);
|
||||
} else {
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
if (node.interfaceTarget != null) {
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
}
|
||||
|
@ -909,8 +796,6 @@ class _TreeShakerPass1 extends Transformer {
|
|||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver, node.value]);
|
||||
} else {
|
||||
node.interfaceTarget = fieldMorpher
|
||||
.adjustInstanceCallTarget(node.interfaceTarget, isSetter: true);
|
||||
if (node.interfaceTarget != null) {
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
}
|
||||
|
@ -924,8 +809,6 @@ class _TreeShakerPass1 extends Transformer {
|
|||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall(_flattenArguments(node.arguments));
|
||||
} else {
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
if (node.interfaceTarget != null) {
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
}
|
||||
|
@ -939,8 +822,6 @@ class _TreeShakerPass1 extends Transformer {
|
|||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([]);
|
||||
} else {
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
if (node.interfaceTarget != null) {
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
}
|
||||
|
@ -954,8 +835,6 @@ class _TreeShakerPass1 extends Transformer {
|
|||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.value]);
|
||||
} else {
|
||||
node.interfaceTarget = fieldMorpher
|
||||
.adjustInstanceCallTarget(node.interfaceTarget, isSetter: true);
|
||||
if (node.interfaceTarget != null) {
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
}
|
||||
|
@ -1001,11 +880,7 @@ class _TreeShakerPass1 extends Transformer {
|
|||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.value]);
|
||||
} else {
|
||||
final target = node.target;
|
||||
assertx(shaker.isMemberBodyReachable(target), details: node);
|
||||
if (target is Field && !shaker.retainField(target)) {
|
||||
return node.value;
|
||||
}
|
||||
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
@ -1017,7 +892,7 @@ class _TreeShakerPass1 extends Transformer {
|
|||
return _makeUnreachableCall(
|
||||
_flattenArguments(node.arguments, receiver: node.receiver));
|
||||
} else {
|
||||
assertx(shaker.isMemberBodyReachable(node.target), details: node);
|
||||
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
@ -1028,10 +903,7 @@ class _TreeShakerPass1 extends Transformer {
|
|||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver]);
|
||||
} else {
|
||||
final target = node.target;
|
||||
assertx(shaker.isMemberBodyReachable(target), details: node);
|
||||
assertx(target is! Field || shaker.isFieldGetterReachable(target),
|
||||
details: node);
|
||||
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
@ -1042,9 +914,7 @@ class _TreeShakerPass1 extends Transformer {
|
|||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver, node.value]);
|
||||
} else {
|
||||
assertx(shaker.isMemberBodyReachable(node.target), details: node);
|
||||
node.target =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.target, isSetter: true);
|
||||
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
@ -1087,20 +957,12 @@ class _TreeShakerPass1 extends Transformer {
|
|||
}
|
||||
|
||||
@override
|
||||
TreeNode visitFieldInitializer(FieldInitializer node) {
|
||||
visitFieldInitializer(FieldInitializer node) {
|
||||
node.transformChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableInitializer([node.value]);
|
||||
} else {
|
||||
assertx(shaker.isMemberBodyReachable(node.field), details: node.field);
|
||||
if (!shaker.retainField(node.field)) {
|
||||
if (mayHaveSideEffects(node.value)) {
|
||||
return LocalInitializer(
|
||||
VariableDeclaration(null, initializer: node.value));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
@ -1373,26 +1235,3 @@ class _TreeShakerConstantVisitor extends ConstantVisitor<Null> {
|
|||
constant.type.accept(typeVisitor);
|
||||
}
|
||||
}
|
||||
|
||||
bool mayHaveSideEffects(Expression node) {
|
||||
if (node is BasicLiteral ||
|
||||
node is ConstantExpression ||
|
||||
node is ThisExpression) {
|
||||
return false;
|
||||
}
|
||||
if (node is VariableGet && !node.variable.isLate) {
|
||||
return false;
|
||||
}
|
||||
if (node is StaticGet) {
|
||||
final target = node.target;
|
||||
if (target is Field && !target.isLate) {
|
||||
final initializer = target.initializer;
|
||||
if (initializer == null ||
|
||||
initializer is BasicLiteral ||
|
||||
initializer is ConstantExpression) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -10,9 +10,7 @@ import 'package:front_end/src/api_unstable/vm.dart'
|
|||
CompilerOptions,
|
||||
DiagnosticMessage,
|
||||
computePlatformBinariesLocation,
|
||||
kernelForProgram,
|
||||
parseExperimentalArguments,
|
||||
parseExperimentalFlags;
|
||||
kernelForProgram;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/text/ast_to_text.dart' show Printer;
|
||||
import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
|
||||
|
@ -37,7 +35,6 @@ class TestingVmTarget extends VmTarget {
|
|||
Future<Component> compileTestCaseToKernelProgram(Uri sourceUri,
|
||||
{Target target,
|
||||
bool enableSuperMixins = false,
|
||||
List<String> experimentalFlags,
|
||||
Map<String, String> environmentDefines}) async {
|
||||
final platformKernel =
|
||||
computePlatformBinariesLocation().resolve('vm_platform_strong.dill');
|
||||
|
@ -48,11 +45,6 @@ Future<Component> compileTestCaseToKernelProgram(Uri sourceUri,
|
|||
..target = target
|
||||
..linkedDependencies = <Uri>[platformKernel]
|
||||
..environmentDefines = environmentDefines
|
||||
..experimentalFlags =
|
||||
parseExperimentalFlags(parseExperimentalArguments(experimentalFlags),
|
||||
onError: (String message) {
|
||||
throw message;
|
||||
})
|
||||
..onDiagnostic = (DiagnosticMessage message) {
|
||||
fail("Compilation error: ${message.plainTextFormatted.join('\n')}");
|
||||
};
|
||||
|
|
|
@ -18,10 +18,10 @@ import '../../common_test_utils.dart';
|
|||
|
||||
final String pkgVmDir = Platform.script.resolve('../../..').toFilePath();
|
||||
|
||||
runTestCase(Uri source, List<String> experimentalFlags) async {
|
||||
runTestCase(Uri source) async {
|
||||
final target = new TestingVmTarget(new TargetFlags());
|
||||
Component component = await compileTestCaseToKernelProgram(source,
|
||||
target: target, experimentalFlags: experimentalFlags);
|
||||
Component component =
|
||||
await compileTestCaseToKernelProgram(source, target: target);
|
||||
|
||||
final coreTypes = new CoreTypes(component);
|
||||
|
||||
|
@ -44,10 +44,7 @@ main() {
|
|||
.listSync(recursive: true, followLinks: false)
|
||||
.reversed) {
|
||||
if (entry.path.endsWith(".dart")) {
|
||||
final List<String> experimentalFlags = [
|
||||
if (entry.path.endsWith("_nnbd.dart")) 'non-nullable',
|
||||
];
|
||||
test(entry.path, () => runTestCase(entry.uri, experimentalFlags));
|
||||
test(entry.path, () => runTestCase(entry.uri));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -15,7 +15,7 @@ class _Vector extends core::Object {
|
|||
[@vm.procedure-attributes.metadata=getterCalledDynamically:false,hasTearOffUses:false,methodOrSetterSelectorId:1] operator []([@vm.inferred-type.metadata=!] core::int* i) → core::double*
|
||||
return [@vm.direct-call.metadata=dart.typed_data::_Float64List::[]] [@vm.inferred-type.metadata=dart.core::_Double (skip check)] [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}.{core::List::[]}([@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}([@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] this.{self::_Vector::_offset}));
|
||||
[@vm.procedure-attributes.metadata=getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2] operator []=([@vm.inferred-type.metadata=dart.core::_OneByteString] core::int* i, core::double* value) → void {
|
||||
let dynamic #t1 = [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements} in let dynamic #t2 = [@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] this.{self::_Vector::_offset} in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
|
||||
let dynamic #t1 = [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements} in let dynamic #t2 = i in let dynamic #t3 = [@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] this.{self::_Vector::_offset} in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
|
||||
}
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] operator *([@vm.inferred-type.metadata=#lib::_Vector?] self::_Vector* a) → core::double* {
|
||||
core::double* result = 0.0;
|
||||
|
@ -27,7 +27,7 @@ class _Vector extends core::Object {
|
|||
[@vm.inferred-type.metadata=#lib::_Vector?]static field self::_Vector* v = new self::_Vector::•(10);
|
||||
[@vm.inferred-type.metadata=dart.core::_Double?]static field core::double* x = 0.0;
|
||||
static method main(core::List<core::String*>* args) → dynamic {
|
||||
core::Stopwatch* timer = let final core::Stopwatch* #t3 = new core::Stopwatch::•() in let final void #t4 = [@vm.direct-call.metadata=dart.core::Stopwatch::start] [@vm.inferred-type.metadata=!? (skip check)] #t3.{core::Stopwatch::start}() in #t3;
|
||||
core::Stopwatch* timer = let final core::Stopwatch* #t4 = new core::Stopwatch::•() in let final void #t5 = [@vm.direct-call.metadata=dart.core::Stopwatch::start] [@vm.inferred-type.metadata=!? (skip check)] #t4.{core::Stopwatch::start}() in #t4;
|
||||
for (core::int* i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation::<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(100000000); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1)) {
|
||||
self::x = [@vm.direct-call.metadata=dart.core::_Double::+??] [@vm.inferred-type.metadata=dart.core::_Double (skip check)] [@vm.inferred-type.metadata=dart.core::_Double?] self::x.{core::double::+}([@vm.direct-call.metadata=#lib::_Vector::*??] [@vm.inferred-type.metadata=dart.core::_Double (skip check)] [@vm.inferred-type.metadata=#lib::_Vector?] self::v.{self::_Vector::*}([@vm.inferred-type.metadata=#lib::_Vector?] self::v));
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class B extends core::Object implements self::I {
|
|||
[@vm.inferred-type.metadata=#lib::B?]static field self::I* ii = new self::B::•();
|
||||
static method bar([@vm.inferred-type.metadata=#lib::B?] self::I* i) → void {
|
||||
if(i is self::A*) {
|
||||
throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
|
||||
let dynamic #t1 = i{self::A*} in let dynamic #t2 = 42 in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
|
||||
}
|
||||
}
|
||||
static method main(core::List<core::String*>* args) → dynamic {
|
||||
|
|
|
@ -2,6 +2,8 @@ library #lib;
|
|||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
abstract class A extends core::Object {
|
||||
}
|
||||
class B extends core::Object {
|
||||
constructor •() → self::B*
|
||||
: super core::Object::•() {
|
||||
|
@ -14,7 +16,8 @@ class C extends core::Object {
|
|||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
[@vm.inferred-type.metadata=dart.core::Null? (value: null)]static field self::A* field = throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
|
||||
static method main() → void {
|
||||
null;
|
||||
self::field = null;
|
||||
[@vm.direct-call.metadata=#lib::C::instanceField] [@vm.inferred-type.metadata=!? (skip check)] new self::C::•().{self::C::instanceField} = null;
|
||||
}
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
|
||||
// Test for tree shaking of write-only fields.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
foo() {}
|
||||
|
||||
class A {
|
||||
// Should be removed.
|
||||
var unused1;
|
||||
|
||||
// Should be removed.
|
||||
var unused2 = 42;
|
||||
|
||||
// Not removed due to a non-trivial initializer.
|
||||
var unused3 = foo();
|
||||
}
|
||||
|
||||
class B {
|
||||
// Should be removed.
|
||||
var unused4;
|
||||
|
||||
// Should be removed.
|
||||
var unused5;
|
||||
|
||||
B(this.unused4) : unused5 = foo();
|
||||
}
|
||||
|
||||
class C<T> {
|
||||
// Should be replaced with setter.
|
||||
T bar;
|
||||
}
|
||||
|
||||
class D implements C<int> {
|
||||
// Should be replaced with setter.
|
||||
int bar;
|
||||
}
|
||||
|
||||
class E {
|
||||
// Should be replaced with getter.
|
||||
final int bar;
|
||||
|
||||
E(this.bar);
|
||||
}
|
||||
|
||||
class F implements E {
|
||||
int get bar => 42;
|
||||
}
|
||||
|
||||
class G {
|
||||
// Not removed because used in a constant.
|
||||
final int bazz;
|
||||
|
||||
const G(this.bazz);
|
||||
}
|
||||
|
||||
class H {
|
||||
// Should be replaced with setter.
|
||||
int unused6;
|
||||
}
|
||||
|
||||
class I extends H {
|
||||
foo() {
|
||||
super.unused6 = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Should be removed.
|
||||
int unusedStatic7 = foo();
|
||||
|
||||
void main() {
|
||||
new A();
|
||||
new B('hi');
|
||||
|
||||
C<num> c = new D();
|
||||
Expect.throws(() {
|
||||
c.bar = 3.14;
|
||||
});
|
||||
|
||||
E e = new F();
|
||||
Expect.equals(42, e.bar);
|
||||
|
||||
Expect.isTrue(!identical(const G(1), const G(2)));
|
||||
|
||||
new I().foo();
|
||||
|
||||
unusedStatic7 = 5;
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "package:expect/expect.dart" as exp;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
class A extends core::Object {
|
||||
[@vm.inferred-type.metadata=dart.core::Null? (value: null)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] field dynamic unused3 = [@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::foo();
|
||||
synthetic constructor •() → self::A*
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
class B extends core::Object {
|
||||
constructor •([@vm.inferred-type.metadata=dart.core::_OneByteString (value: hi)] dynamic unused4) → self::B*
|
||||
: dynamic #t1 = [@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::foo(), super core::Object::•()
|
||||
;
|
||||
}
|
||||
abstract class C<T extends core::Object* = dynamic> extends core::Object {
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] abstract set bar(generic-covariant-impl self::C::T* value) → void;
|
||||
}
|
||||
class D extends core::Object implements self::C<core::int*> {
|
||||
synthetic constructor •() → self::D*
|
||||
: super core::Object::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] set bar(generic-covariant-impl core::int* value) → void;
|
||||
}
|
||||
abstract class E extends core::Object {
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] abstract get bar() → core::int*;
|
||||
}
|
||||
class F extends core::Object implements self::E {
|
||||
synthetic constructor •() → self::F*
|
||||
: super core::Object::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] get bar() → core::int*
|
||||
return 42;
|
||||
}
|
||||
class G extends core::Object /*hasConstConstructor*/ {
|
||||
[@vm.inferred-type.metadata=dart.core::_Smi] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] final field core::int* bazz;
|
||||
}
|
||||
abstract class H extends core::Object {
|
||||
synthetic constructor •() → self::H*
|
||||
: super core::Object::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7] set unused6(core::int* value) → void;
|
||||
}
|
||||
class I extends self::H {
|
||||
synthetic constructor •() → self::I*
|
||||
: super self::H::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:8,getterSelectorId:9] method foo() → dynamic {
|
||||
super.{self::H::unused6} = 3;
|
||||
}
|
||||
}
|
||||
static method foo() → dynamic {}
|
||||
static method main() → void {
|
||||
new self::A::•();
|
||||
new self::B::•("hi");
|
||||
self::C<core::num*>* c = new self::D::•();
|
||||
exp::Expect::throws<dynamic>(() → core::Null? {
|
||||
[@vm.call-site-attributes.metadata=receiverType:#lib::C<dart.core::num*>*] [@vm.direct-call.metadata=#lib::D::bar] c.{self::C::bar} = 3.14;
|
||||
});
|
||||
self::E* e = new self::F::•();
|
||||
exp::Expect::equals(42, [@vm.direct-call.metadata=#lib::F::bar] [@vm.inferred-type.metadata=dart.core::_Smi (value: 42)] e.{self::E::bar});
|
||||
exp::Expect::isTrue(![@vm.inferred-type.metadata=dart.core::bool] core::identical(#C2, #C4));
|
||||
[@vm.direct-call.metadata=#lib::I::foo] [@vm.inferred-type.metadata=!? (skip check)] new self::I::•().{self::I::foo}();
|
||||
5;
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
|
||||
// Test for tree shaking of write-only late fields.
|
||||
// This test requires non-nullable experiment.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
foo() {}
|
||||
|
||||
class A {
|
||||
// Should be replaced with setter.
|
||||
late int x;
|
||||
|
||||
use() {
|
||||
x = 3;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
// Should be retained.
|
||||
late final int x;
|
||||
|
||||
use() {
|
||||
x = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Should be removed.
|
||||
late int staticLateA;
|
||||
|
||||
// Should be retained.
|
||||
late final int staticLateB;
|
||||
|
||||
void main() {
|
||||
new A().use();
|
||||
new B().use();
|
||||
|
||||
staticLateA = 4;
|
||||
staticLateB = 4;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
library #lib /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
class A extends core::Object {
|
||||
synthetic constructor •() → self::A
|
||||
: super core::Object::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method use() → dynamic {
|
||||
[@vm.direct-call.metadata=#lib::A::x] [@vm.inferred-type.metadata=!? (skip check)] this.{self::A::x} = 3;
|
||||
}
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] set /*isNullableByDefault*/ x(core::int value) → void;
|
||||
}
|
||||
class B extends core::Object {
|
||||
[@vm.unreachable.metadata=] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] late final field core::int x;
|
||||
synthetic constructor •() → self::B
|
||||
: super core::Object::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method use() → dynamic {
|
||||
[@vm.inferred-type.metadata=!? (skip check)] this.{self::B::x} = 3;
|
||||
}
|
||||
}
|
||||
[@vm.inferred-type.metadata=dart.core::_Smi?]late static final field core::int staticLateB;
|
||||
static method main() → void {
|
||||
[@vm.direct-call.metadata=#lib::A::use] [@vm.inferred-type.metadata=!? (skip check)] new self::A::•().{self::A::use}();
|
||||
[@vm.direct-call.metadata=#lib::B::use] [@vm.inferred-type.metadata=!? (skip check)] new self::B::•().{self::B::use}();
|
||||
4;
|
||||
self::staticLateB = 4;
|
||||
}
|
|
@ -8,15 +8,7 @@ import 'package:observatory/service_io.dart';
|
|||
import 'package:unittest/unittest.dart';
|
||||
import 'test_helper.dart';
|
||||
|
||||
// Make sure these variables are not removed by the tree shaker.
|
||||
@pragma("vm:entry-point")
|
||||
var cleanBlock;
|
||||
@pragma("vm:entry-point")
|
||||
var copyingBlock;
|
||||
@pragma("vm:entry-point")
|
||||
var fullBlock;
|
||||
@pragma("vm:entry-point")
|
||||
var fullBlockWithChain;
|
||||
var cleanBlock, copyingBlock, fullBlock, fullBlockWithChain;
|
||||
|
||||
Function genCleanBlock() {
|
||||
block(x) => x;
|
||||
|
|
|
@ -12,86 +12,62 @@ import 'test_helper.dart';
|
|||
|
||||
// small example from [Lenguaer & Tarjan 1979]
|
||||
class R {
|
||||
// All fields are marked with @pragma("vm:entry-point")
|
||||
// in order to make sure they are not removed by the tree shaker
|
||||
// even though they are never read.
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
@pragma("vm:entry-point")
|
||||
var z;
|
||||
}
|
||||
|
||||
class A {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class B {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
@pragma("vm:entry-point")
|
||||
var z;
|
||||
}
|
||||
|
||||
class C {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
class D {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class E {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class F {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class G {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
class H {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
class I {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class J {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class K {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
class L {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,86 +21,62 @@ double getDoubleWithHeapObjectTag() {
|
|||
// small example from [Lenguaer & Tarjan 1979]
|
||||
class R {
|
||||
final double fld = getDoubleWithHeapObjectTag();
|
||||
// Fields are marked with @pragma("vm:entry-point")
|
||||
// in order to make sure they are not removed by the tree shaker
|
||||
// even though they are never read.
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
@pragma("vm:entry-point")
|
||||
var z;
|
||||
}
|
||||
|
||||
class A {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class B {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
@pragma("vm:entry-point")
|
||||
var z;
|
||||
}
|
||||
|
||||
class C {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
class D {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class E {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class F {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class G {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
class H {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
class I {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class J {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
class K {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
class L {
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,10 +9,7 @@ import 'test_helper.dart';
|
|||
|
||||
class _TestClass {
|
||||
_TestClass(this.x, this.y);
|
||||
// Make sure these fields are not removed by the tree shaker.
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,14 +9,10 @@ import 'test_helper.dart';
|
|||
|
||||
class _TestClass {
|
||||
_TestClass(this.x, this.y);
|
||||
// Make sure these fields are not removed by the tree shaker.
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
var myVar;
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
|
|
|
@ -9,10 +9,7 @@ import 'test_helper.dart';
|
|||
|
||||
class _TestClass {
|
||||
_TestClass();
|
||||
// Make sure these fields are not removed by the tree shaker.
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@ import 'package:unittest/unittest.dart';
|
|||
import 'test_helper.dart';
|
||||
|
||||
class Node {
|
||||
// Make sure this field is not removed by the tree shaker.
|
||||
@pragma("vm:entry-point")
|
||||
var edge;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,17 +9,12 @@ import 'package:unittest/unittest.dart';
|
|||
import 'test_helper.dart';
|
||||
|
||||
class Super {
|
||||
// Make sure these fields are not removed by the tree shaker.
|
||||
@pragma("vm:entry-point")
|
||||
var z = 1;
|
||||
@pragma("vm:entry-point")
|
||||
var y = 2;
|
||||
}
|
||||
|
||||
class Sub extends Super {
|
||||
@pragma("vm:entry-point")
|
||||
var y = 3;
|
||||
@pragma("vm:entry-point")
|
||||
var x = 4;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,10 +9,7 @@ import 'package:unittest/unittest.dart';
|
|||
import 'test_helper.dart';
|
||||
|
||||
class Foo {
|
||||
// Make sure these fields are not removed by the tree shaker.
|
||||
@pragma("vm:entry-point")
|
||||
dynamic left;
|
||||
@pragma("vm:entry-point")
|
||||
dynamic right;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,7 @@ import 'test_helper.dart';
|
|||
import 'service_test_common.dart';
|
||||
|
||||
class Pair {
|
||||
// Make sure these fields are not removed by the tree shaker.
|
||||
@pragma("vm:entry-point")
|
||||
var x;
|
||||
@pragma("vm:entry-point")
|
||||
var y;
|
||||
var x, y;
|
||||
}
|
||||
|
||||
var p1;
|
||||
|
|
|
@ -8,10 +8,7 @@ import 'package:observatory/service_io.dart';
|
|||
import 'package:unittest/unittest.dart';
|
||||
import 'test_helper.dart';
|
||||
|
||||
// Make sure these variables are not removed by the tree shaker.
|
||||
@pragma("vm:entry-point")
|
||||
var regex0;
|
||||
@pragma("vm:entry-point")
|
||||
var regex;
|
||||
|
||||
void script() {
|
||||
|
|
|
@ -60,11 +60,7 @@ class D extends C with M {
|
|||
}
|
||||
|
||||
class E {
|
||||
// Make sure E.fun is not removed by the tree shaker, as this test verifies
|
||||
// that an object with a tear-off in E.fun cannot be sent.
|
||||
@pragma("vm:entry-point")
|
||||
Function fun;
|
||||
|
||||
E(this.fun);
|
||||
|
||||
static fooFun() => 499;
|
||||
|
|
Loading…
Reference in a new issue