From 671717b578e8cf7f107ddc5184e6696be0668dd4 Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Wed, 19 Oct 2022 17:49:47 +0000 Subject: [PATCH] [vm/aot] Support dynamic record field access in TFA TEST=language/records/simple/dynamic_field_access_test Issue: https://github.com/dart-lang/sdk/issues/49719 Change-Id: I811db5c649988cbadf7ab29e5c4c70366f55e86b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262845 Reviewed-by: Slava Egorov Commit-Queue: Alexander Markov --- pkg/kernel/lib/target/targets.dart | 3 +- pkg/vm/lib/target/vm.dart | 6 +- .../transformations/type_flow/analysis.dart | 118 ++++++++++++++---- .../type_flow/native_code.dart | 6 + pkg/vm/lib/transformations/type_flow/rta.dart | 6 + .../transformations/type_flow/summary.dart | 2 +- .../type_flow/summary_collector.dart | 63 ++++++---- .../type_flow/transformer.dart | 13 +- .../lib/transformations/type_flow/types.dart | 15 ++- .../lib/transformations/type_flow/utils.dart | 11 ++ .../type_flow/summary_collector_test.dart | 39 ++++-- .../type_flow/transformer_test.dart | 21 +++- .../type_flow/summary_collector/records.dart | 13 ++ .../summary_collector/records.dart.expect | 22 ++++ .../summary_collector/records.dart.options | 1 + .../type_flow/transformer/records.dart | 23 ++++ .../type_flow/transformer/records.dart.expect | 29 +++++ .../transformer/records.dart.options | 1 + tools/generate_package_config.dart | 40 +++--- 19 files changed, 330 insertions(+), 102 deletions(-) create mode 100644 pkg/vm/testcases/transformations/type_flow/summary_collector/records.dart create mode 100644 pkg/vm/testcases/transformations/type_flow/summary_collector/records.dart.expect create mode 100644 pkg/vm/testcases/transformations/type_flow/summary_collector/records.dart.options create mode 100644 pkg/vm/testcases/transformations/type_flow/transformer/records.dart create mode 100644 pkg/vm/testcases/transformations/type_flow/transformer/records.dart.expect create mode 100644 pkg/vm/testcases/transformations/type_flow/transformer/records.dart.options diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart index e8eeff0b560..b4758f7a724 100644 --- a/pkg/kernel/lib/target/targets.dart +++ b/pkg/kernel/lib/target/targets.dart @@ -542,8 +542,7 @@ abstract class Target { Class? concreteConstMapLiteralClass(CoreTypes coreTypes) => null; Class? concreteSetLiteralClass(CoreTypes coreTypes) => null; Class? concreteConstSetLiteralClass(CoreTypes coreTypes) => null; - Class? concreteRecordLiteralClass(CoreTypes coreTypes) => null; - Class? concreteConstRecordLiteralClass(CoreTypes coreTypes) => null; + Class? concreteRecordClass(CoreTypes coreTypes) => null; Class? concreteIntLiteralClass(CoreTypes coreTypes, int value) => null; Class? concreteDoubleLiteralClass(CoreTypes coreTypes, double value) => null; diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart index a5402c647c0..4200b8a5df9 100644 --- a/pkg/vm/lib/target/vm.dart +++ b/pkg/vm/lib/target/vm.dart @@ -453,14 +453,10 @@ class VmTarget extends Target { } @override - Class concreteRecordLiteralClass(CoreTypes coreTypes) { + Class concreteRecordClass(CoreTypes coreTypes) { return _record ??= coreTypes.index.getClass('dart:core', '_Record'); } - @override - Class concreteConstRecordLiteralClass(CoreTypes coreTypes) => - concreteRecordLiteralClass(coreTypes); - @override Class? concreteIntLiteralClass(CoreTypes coreTypes, int value) { const int bitsPerInt32 = 32; diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart index 224988b9eab..d0fc23fa8ff 100644 --- a/pkg/vm/lib/transformations/type_flow/analysis.dart +++ b/pkg/vm/lib/transformations/type_flow/analysis.dart @@ -538,15 +538,10 @@ class _DispatchableInvocation extends _Invocation { } } - // TODO(alexmarkov): Consider caching targets for Null type. void _collectTargetsForNull(Map targets, TypeFlowAnalysis typeFlowAnalysis) { - Class nullClass = - typeFlowAnalysis.environment.coreTypes.deprecatedNullClass; - - Member? target = typeFlowAnalysis.hierarchyCache.hierarchy - .getDispatchTarget(nullClass, selector.name, setter: selector.isSetter); - + final Member? target = typeFlowAnalysis.hierarchyCache._nullTFClass + .getDispatchTarget(selector); if (target != null) { if (kPrintTrace) { tracePrint("Found $target for null receiver"); @@ -559,9 +554,12 @@ class _DispatchableInvocation extends _Invocation { ConcreteType receiver, Map targets, TypeFlowAnalysis typeFlowAnalysis) { - final TFClass cls = receiver.cls; + final cls = receiver.cls as _TFClassImpl; - Member? target = (cls as _TFClassImpl).getDispatchTarget(selector); + Member? target = cls.getDispatchTarget(selector); + if (cls.hasMutableDispatchTargets) { + cls.dependencyTracker.addDependentInvocation(this); + } if (target != null) { if (kPrintTrace) { @@ -1011,13 +1009,15 @@ class _TFClassImpl extends TFClass { late final Map _dispatchTargetsNonSetters = _initDispatchTargets(false); final _DependencyTracker dependencyTracker = new _DependencyTracker(); + final bool hasMutableDispatchTargets; /// Flag indicating if this class has a noSuchMethod() method not inherited /// from Object. /// Lazy initialized by ClassHierarchyCache.hasNonTrivialNoSuchMethod(). bool? hasNonTrivialNoSuchMethod; - _TFClassImpl(int id, Class classNode, this.superclass, this.supertypes) + _TFClassImpl(int id, Class classNode, this.superclass, this.supertypes, + {required this.hasMutableDispatchTargets}) : super(id, classNode) { supertypes.add(this); } @@ -1056,6 +1056,7 @@ class _TFClassImpl extends TFClass { } void addAllocatedSubtype(_TFClassImpl subType) { + assert(subType == this || !hasMutableDispatchTargets); _allocatedSubtypes.add(subType); _specializedConeType = null; // Reset cached specialization. } @@ -1064,6 +1065,7 @@ class _TFClassImpl extends TFClass { Map targets; final superclass = this.superclass; if (superclass != null) { + assert(!superclass.hasMutableDispatchTargets); targets = Map.from(setters ? superclass._dispatchTargetsSetters : superclass._dispatchTargetsNonSetters); @@ -1093,6 +1095,15 @@ class _TFClassImpl extends TFClass { : _dispatchTargetsNonSetters)[selector.name]; } + void _addField(Field field) { + assert(hasMutableDispatchTargets); + assert(!field.isStatic && !field.isAbstract); + _dispatchTargetsNonSetters[field.name] = field; + if (field.hasSetter) { + _dispatchTargetsSetters[field.name] = field; + } + } + String dump() => "$this {supers: $supertypes}"; } @@ -1167,7 +1178,6 @@ class GenericInterfacesInfoImpl implements GenericInterfacesInfo { // TODO(alexmarkov): Rename to _TypeHierarchyImpl. class _ClassHierarchyCache extends TypeHierarchy { final TypeFlowAnalysis _typeFlowAnalysis; - final ClosedWorldClassHierarchy hierarchy; final TypeEnvironment environment; final Set allocatedClasses = new Set(); final Map classes = {}; @@ -1178,6 +1188,10 @@ class _ClassHierarchyCache extends TypeHierarchy { static final Name noSuchMethodName = new Name("noSuchMethod"); + // Class of all record instances (could be synthetic if Target + // doesn't provide one). + final Class recordClass; + /// Class hierarchy is sealed after analysis is finished. /// Once it is sealed, no new allocated classes may be added and no new /// targets of invocations may appear. @@ -1189,10 +1203,31 @@ class _ClassHierarchyCache extends TypeHierarchy { final Map _dynamicTargets = {}; - _ClassHierarchyCache(this._typeFlowAnalysis, this.hierarchy, - this.genericInterfacesInfo, this.environment, bool nullSafety) - : objectNoSuchMethod = hierarchy.getDispatchTarget( - environment.coreTypes.objectClass, noSuchMethodName)!, + final Map _recordFields = {}; + + @override + late final Type recordType = addAllocatedClass(recordClass); + + late final _TFClassImpl _recordTFClass = getTFClass(recordClass); + + late final _TFClassImpl _objectTFClass = + getTFClass(environment.coreTypes.objectClass); + + late final _TFClassImpl _nullTFClass = + getTFClass(environment.coreTypes.deprecatedNullClass); + + _ClassHierarchyCache(this._typeFlowAnalysis, this.genericInterfacesInfo, + this.environment, bool nullSafety) + : objectNoSuchMethod = environment.coreTypes.index + .getProcedure('dart:core', 'Object', 'noSuchMethod'), + recordClass = _typeFlowAnalysis.target + .concreteRecordClass(environment.coreTypes) ?? + Class( + name: "&&Record", + implementedTypes: [ + Supertype(environment.coreTypes.recordClass, []) + ], + fileUri: artificialNodeUri), super(environment.coreTypes, nullSafety); @override @@ -1208,7 +1243,8 @@ class _ClassHierarchyCache extends TypeHierarchy { Class? superclassNode = c.superclass; _TFClassImpl? superclass = superclassNode != null ? getTFClass(superclassNode) : null; - return new _TFClassImpl(++_classIdCounter, c, superclass, supertypes); + return new _TFClassImpl(++_classIdCounter, c, superclass, supertypes, + hasMutableDispatchTargets: c == recordClass); } ConcreteType addAllocatedClass(Class cl) { @@ -1286,7 +1322,7 @@ class _ClassHierarchyCache extends TypeHierarchy { bool? value = classImpl.hasNonTrivialNoSuchMethod; if (value == null) { classImpl.hasNonTrivialNoSuchMethod = value = - (hierarchy.getDispatchTarget(c.classNode, noSuchMethodName) != + (classImpl._dispatchTargetsNonSetters[noSuchMethodName] != objectNoSuchMethod); } return value; @@ -1297,11 +1333,7 @@ class _ClassHierarchyCache extends TypeHierarchy { } _DynamicTargetSet _createDynamicTargetSet(DynamicSelector selector) { - // TODO(alexmarkov): consider caching the set of Object selectors. - final isObjectMethod = (hierarchy.getDispatchTarget( - _typeFlowAnalysis.environment.coreTypes.objectClass, selector.name, - setter: selector.isSetter) != - null); + final isObjectMethod = _objectTFClass.getDispatchTarget(selector) != null; final targetSet = new _DynamicTargetSet(selector, isObjectMethod); for (Class c in allocatedClasses) { @@ -1313,8 +1345,7 @@ class _ClassHierarchyCache extends TypeHierarchy { void _addDynamicTarget(Class c, _DynamicTargetSet targetSet) { assert(!_sealed); final selector = targetSet.selector; - final member = hierarchy.getDispatchTarget(c, selector.name, - setter: selector.isSetter); + final member = getTFClass(c).getDispatchTarget(selector); if (member != null) { if (targetSet.targets.add(member)) { targetSet.invalidateDependentInvocations(_typeFlowAnalysis.workList); @@ -1322,6 +1353,35 @@ class _ClassHierarchyCache extends TypeHierarchy { } } + Field getRecordField(String name) { + return _recordFields[name] ??= _addRecordField(Name(name)); + } + + Field _addRecordField(Name name) { + assert(!_sealed); + final Field field = Field.immutable(name, fileUri: artificialNodeUri); + field.parent = recordClass; + // Add field to the record class for future queries. + _recordTFClass._addField(field); + _recordTFClass.dependencyTracker + .invalidateDependentInvocations(_typeFlowAnalysis.workList); + // Update dynamic target sets collected so far. + _DynamicTargetSet? targetSet = + _dynamicTargets[DynamicSelector(CallKind.PropertyGet, name)]; + if (targetSet != null) { + if (targetSet.targets.add(field)) { + targetSet.invalidateDependentInvocations(_typeFlowAnalysis.workList); + } + } + targetSet = _dynamicTargets[DynamicSelector(CallKind.Method, name)]; + if (targetSet != null) { + if (targetSet.targets.add(field)) { + targetSet.invalidateDependentInvocations(_typeFlowAnalysis.workList); + } + } + return field; + } + @override List flattenedTypeArgumentsFor(Class klass) => genericInterfacesInfo.flattenedTypeArgumentsFor(klass); @@ -1553,8 +1613,8 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler { : annotationMatcher = matcher ?? new ConstantPragmaAnnotationParser(coreTypes, target) { nativeCodeOracle = new NativeCodeOracle(libraryIndex, annotationMatcher); - hierarchyCache = new _ClassHierarchyCache(this, hierarchy, - _genericInterfacesInfo, environment, target.flags.enableNullSafety); + hierarchyCache = new _ClassHierarchyCache(this, _genericInterfacesInfo, + environment, target.flags.enableNullSafety); summaryCollector = new SummaryCollector( target, environment, @@ -1769,6 +1829,12 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler { return hierarchyCache.addAllocatedClass(c); } + @override + Field getRecordPositionalField(int pos) => getRecordNamedField("\$$pos"); + + @override + Field getRecordNamedField(String name) => hierarchyCache.getRecordField(name); + @override void recordMemberCalledViaInterfaceSelector(Member target) { _calledViaInterfaceSelector.add(target); diff --git a/pkg/vm/lib/transformations/type_flow/native_code.dart b/pkg/vm/lib/transformations/type_flow/native_code.dart index 450233d7780..12292907246 100644 --- a/pkg/vm/lib/transformations/type_flow/native_code.dart +++ b/pkg/vm/lib/transformations/type_flow/native_code.dart @@ -23,6 +23,12 @@ abstract class EntryPointsListener { /// Add instantiation of the given class. ConcreteType addAllocatedClass(Class c); + /// Returns synthetic Field representing positional field of a record. + Field getRecordPositionalField(int pos); + + /// Returns synthetic Field representing named field of a record. + Field getRecordNamedField(String name); + /// Record the fact that given member is called via interface selector /// (not dynamically, and not from `this`). void recordMemberCalledViaInterfaceSelector(Member target); diff --git a/pkg/vm/lib/transformations/type_flow/rta.dart b/pkg/vm/lib/transformations/type_flow/rta.dart index 999257bb5e2..7624a19c98e 100644 --- a/pkg/vm/lib/transformations/type_flow/rta.dart +++ b/pkg/vm/lib/transformations/type_flow/rta.dart @@ -526,6 +526,12 @@ class _EntryPointsListenerImpl implements EntryPointsListener { @override ConcreteType addAllocatedClass(Class c) => rta.addAllocatedClass(c); + @override + Field getRecordPositionalField(int pos) => throw 'Unsupported operation'; + + @override + Field getRecordNamedField(String name) => throw 'Unsupported operation'; + @override void recordMemberCalledViaInterfaceSelector(Member target) => throw 'Unsupported operation'; diff --git a/pkg/vm/lib/transformations/type_flow/summary.dart b/pkg/vm/lib/transformations/type_flow/summary.dart index 479884152d8..a49cee7deec 100644 --- a/pkg/vm/lib/transformations/type_flow/summary.dart +++ b/pkg/vm/lib/transformations/type_flow/summary.dart @@ -298,7 +298,7 @@ class Call extends Statement { Member? _monomorphicTarget; - Member? get monomorphicTarget => _monomorphicTarget; + Member? get monomorphicTarget => filterArtificialNode(_monomorphicTarget); bool get isMonomorphic => (_flags & kMonomorphic) != 0; diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart index eec2c708794..28da1826a9f 100644 --- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart +++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart @@ -1622,30 +1622,42 @@ class SummaryCollector extends RecursiveResultVisitor { @override TypeExpr visitRecordLiteral(RecordLiteral node) { - for (var expr in node.positional) { - _visit(expr); + final Type receiver = _typesBuilder.recordType; + for (int i = 0; i < node.positional.length; ++i) { + final Field f = _entryPointsListener.getRecordPositionalField(i); + final TypeExpr value = _visit(node.positional[i]); + final args = Args([receiver, value]); + _makeCall(node, + DirectSelector(f, callKind: CallKind.SetFieldInConstructor), args); } for (var expr in node.named) { - _visit(expr.value); + final Field f = _entryPointsListener.getRecordNamedField(expr.name); + final TypeExpr value = _visit(expr.value); + final args = Args([receiver, value]); + _makeCall(node, + DirectSelector(f, callKind: CallKind.SetFieldInConstructor), args); } - Class? concreteClass = - target.concreteRecordLiteralClass(_environment.coreTypes); - if (concreteClass != null) { - return _entryPointsListener.addAllocatedClass(concreteClass); - } - return _staticType(node); + callSites.remove(node); + return receiver; } @override TypeExpr visitRecordIndexGet(RecordIndexGet node) { - _visit(node.receiver); - return _staticType(node); + final receiver = _visit(node.receiver); + final Field field = + _entryPointsListener.getRecordPositionalField(node.index); + final args = Args([receiver]); + return _makeCall( + node, DirectSelector(field, callKind: CallKind.PropertyGet), args); } @override TypeExpr visitRecordNameGet(RecordNameGet node) { - _visit(node.receiver); - return _staticType(node); + final receiver = _visit(node.receiver); + final Field field = _entryPointsListener.getRecordNamedField(node.name); + final args = Args([receiver]); + return _makeCall( + node, DirectSelector(field, callKind: CallKind.PropertyGet), args); } @override @@ -2594,20 +2606,19 @@ class ConstantAllocationCollector extends ConstantVisitor { @override Type visitRecordConstant(RecordConstant constant) { - for (var value in constant.positional) { - typeFor(value); + final epl = summaryCollector._entryPointsListener; + final Type receiver = summaryCollector._typesBuilder.recordType; + for (int i = 0; i < constant.positional.length; ++i) { + final Field f = epl.getRecordPositionalField(i); + final Type value = typeFor(constant.positional[i]); + epl.addFieldUsedInConstant(f, receiver, value); } - for (var value in constant.named.values) { - typeFor(value); - } - Class? concreteClass = summaryCollector.target - .concreteConstRecordLiteralClass( - summaryCollector._environment.coreTypes); - if (concreteClass != null) { - return summaryCollector._entryPointsListener - .addAllocatedClass(concreteClass); - } - return _getStaticType(constant); + constant.named.forEach((String fieldName, Constant fieldValue) { + final Field f = epl.getRecordNamedField(fieldName); + final Type value = typeFor(fieldValue); + epl.addFieldUsedInConstant(f, receiver, value); + }); + return receiver; } @override diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart index 9cb2422a4e7..69413b3f776 100644 --- a/pkg/vm/lib/transformations/type_flow/transformer.dart +++ b/pkg/vm/lib/transformations/type_flow/transformer.dart @@ -139,8 +139,8 @@ Component transformComponent( final unboxingInfo = new UnboxingInfoManager(typeFlowAnalysis) ..analyzeComponent(component, typeFlowAnalysis, tableSelectorAssigner); - new AnnotateKernel(component, typeFlowAnalysis, treeShaker.fieldMorpher, - tableSelectorAssigner, unboxingInfo) + new AnnotateKernel(component, typeFlowAnalysis, hierarchy, + treeShaker.fieldMorpher, tableSelectorAssigner, unboxingInfo) .visitComponent(component); transformsStopWatch.stop(); @@ -300,6 +300,7 @@ class TFADevirtualization extends Devirtualization { /// Annotates kernel AST with metadata using results of type flow analysis. class AnnotateKernel extends RecursiveVisitor { final TypeFlowAnalysis _typeFlowAnalysis; + final ClassHierarchy hierarchy; final FieldMorpher fieldMorpher; final DirectCallMetadataRepository _directCallMetadataRepository; final InferredTypeMetadataRepository _inferredTypeMetadata; @@ -312,8 +313,8 @@ class AnnotateKernel extends RecursiveVisitor { final Class _intClass; late final Constant _nullConstant = NullConstant(); - AnnotateKernel(Component component, this._typeFlowAnalysis, this.fieldMorpher, - this._tableSelectorAssigner, this._unboxingInfo) + AnnotateKernel(Component component, this._typeFlowAnalysis, this.hierarchy, + this.fieldMorpher, this._tableSelectorAssigner, this._unboxingInfo) : _directCallMetadataRepository = component.metadata[DirectCallMetadataRepository.repositoryTag] as DirectCallMetadataRepository, @@ -429,9 +430,7 @@ class AnnotateKernel extends RecursiveVisitor { // here), then the receiver cannot be _Smi. This heuristic covers most // cases, so we skip these to avoid showering the AST with annotations. if (interfaceTarget == null || - _typeFlowAnalysis.hierarchyCache.hierarchy.isSubtypeOf( - _typeFlowAnalysis.hierarchyCache.coreTypes.intClass, - interfaceTarget.enclosingClass!)) { + hierarchy.isSubtypeOf(_intClass, interfaceTarget.enclosingClass!)) { markReceiverNotInt = true; } } diff --git a/pkg/vm/lib/transformations/type_flow/types.dart b/pkg/vm/lib/transformations/type_flow/types.dart index 0d4c48115db..cb95ac2c48b 100644 --- a/pkg/vm/lib/transformations/type_flow/types.dart +++ b/pkg/vm/lib/transformations/type_flow/types.dart @@ -70,6 +70,8 @@ abstract class TypesBuilder { /// Return [TFClass] corresponding to the given [classNode]. TFClass getTFClass(Class classNode); + late final Type recordType = ConeType(getTFClass(coreTypes.recordClass)); + /// Create a Type which corresponds to a set of instances constrained by /// Dart type annotation [dartType]. /// [canBeNull] can be set to false to further constrain the resulting @@ -88,7 +90,7 @@ abstract class TypesBuilder { result = const AnyType(); } else if (type is RecordType) { // TODO(dartbug.com/49719): support inference of record types - result = const AnyType(); + result = recordType; } else if (type is FutureOrType) { // TODO(alexmarkov): support FutureOr types result = const AnyType(); @@ -575,7 +577,7 @@ class SetType extends Type { return type.intersection(other, typeHierarchy); } } - return EmptyType(); + return const EmptyType(); } else if (other is ConeType) { return typeHierarchy .specializeTypeCone(other.cls, allowWideCone: true) @@ -818,7 +820,8 @@ class ConcreteType extends Type implements Comparable { bool get isRaw => typeArgs == null && constant == null; @override - Class getConcreteClass(TypeHierarchy typeHierarchy) => cls.classNode; + Class? getConcreteClass(TypeHierarchy typeHierarchy) => + filterArtificialNode(cls.classNode); @override bool isSubtypeOf(TypeHierarchy typeHierarchy, Class other) => @@ -989,7 +992,7 @@ class ConcreteType extends Type implements Comparable { return this; } if (!identical(this.cls, other.cls)) { - return EmptyType(); + return const EmptyType(); } if (typeArgs == null && constant == null) { return other; @@ -1177,8 +1180,8 @@ class RuntimeType extends Type { throw "ERROR: RuntimeType does not support specialize."; @override - Class getConcreteClass(TypeHierarchy typeHierarchy) => - throw "ERROR: ConcreteClass does not support getConcreteClass."; + Class? getConcreteClass(TypeHierarchy typeHierarchy) => + throw "ERROR: RuntimeType does not support getConcreteClass."; bool isSubtypeOfRuntimeType(TypeHierarchy typeHierarchy, RuntimeType runtimeType, SubtypeTestKind kind) { diff --git a/pkg/vm/lib/transformations/type_flow/utils.dart b/pkg/vm/lib/transformations/type_flow/utils.dart index ad6ce303c6d..327cc8aa4fd 100644 --- a/pkg/vm/lib/transformations/type_flow/utils.dart +++ b/pkg/vm/lib/transformations/type_flow/utils.dart @@ -425,3 +425,14 @@ bool mayHaveOrSeeSideEffects(Expression node) { } return false; } + +// Dedicated fileUri for artifical nodes created during type flow analysis. +final Uri artificialNodeUri = Uri(scheme: 'tfa-artificial-node'); + +// Returns true if [node] was artificially created during type flow analysis. +bool isArtificialNode(TreeNode node) => + node is FileUriNode && identical(node.fileUri, artificialNodeUri); + +// Returns [node] or null, if node is artifical. +T? filterArtificialNode(T? node) => + (node == null || isArtificialNode(node)) ? null : node; diff --git a/pkg/vm/test/transformations/type_flow/summary_collector_test.dart b/pkg/vm/test/transformations/type_flow/summary_collector_test.dart index 7c96cf388cf..fc359d5767f 100644 --- a/pkg/vm/test/transformations/type_flow/summary_collector_test.dart +++ b/pkg/vm/test/transformations/type_flow/summary_collector_test.dart @@ -4,6 +4,7 @@ import 'dart:io'; +import 'package:front_end/src/api_unstable/vm.dart'; import 'package:kernel/ast.dart'; import 'package:kernel/class_hierarchy.dart'; import 'package:kernel/core_types.dart'; @@ -21,7 +22,7 @@ import 'package:vm/transformations/type_flow/types.dart'; import '../../common_test_utils.dart'; -final String pkgVmDir = Platform.script.resolve('../../..').toFilePath(); +final Uri pkgVmDir = Platform.script.resolve('../../..'); class FakeTypesBuilder extends TypesBuilder { final Map _classes = {}; @@ -51,6 +52,13 @@ class FakeEntryPointsListener implements EntryPointsListener { return new ConcreteType(_typesBuilder.getTFClass(c), null); } + @override + Field getRecordPositionalField(int pos) => getRecordNamedField("\$$pos"); + + @override + Field getRecordNamedField(String name) => + Field.immutable(Name(name), fileUri: dummyUri); + @override void recordMemberCalledViaInterfaceSelector(Member target) {} @@ -96,9 +104,17 @@ class PrintSummaries extends RecursiveVisitor { } } -runTestCase(Uri source) async { +class TestOptions { + static const Option?> enableExperiment = + Option('--enable-experiment', StringListValue()); + + static const List