|  | // Copyright (c) 2018, 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 'dart:io'; | 
|  | import 'package:_fe_analyzer_shared/src/testing/features.dart'; | 
|  | import 'package:expect/async_helper.dart'; | 
|  | import 'package:compiler/src/closure.dart'; | 
|  | import 'package:compiler/src/common.dart'; | 
|  | import 'package:compiler/src/common/elements.dart'; | 
|  | import 'package:compiler/src/compiler.dart'; | 
|  | import 'package:compiler/src/elements/entities.dart'; | 
|  | import 'package:compiler/src/elements/names.dart'; | 
|  | import 'package:compiler/src/elements/types.dart'; | 
|  | import 'package:compiler/src/js_backend/runtime_types_resolution.dart'; | 
|  | import 'package:compiler/src/js_model/js_world.dart'; | 
|  | import 'package:compiler/src/js_model/element_map.dart'; | 
|  | import 'package:compiler/src/kernel/element_map.dart'; | 
|  | import 'package:compiler/src/kernel/kernel_strategy.dart'; | 
|  | import 'package:compiler/src/universe/feature.dart'; | 
|  | import 'package:compiler/src/universe/resolution_world_builder.dart'; | 
|  | import 'package:compiler/src/universe/selector.dart'; | 
|  | import 'package:kernel/ast.dart' as ir; | 
|  | import '../equivalence/check_helpers.dart'; | 
|  | import '../equivalence/id_equivalence.dart'; | 
|  | import '../equivalence/id_equivalence_helper.dart'; | 
|  |  | 
|  | main(List<String> args) { | 
|  | runTests(args); | 
|  | } | 
|  |  | 
|  | runTests(List<String> args, [int? shardIndex]) { | 
|  | asyncTest(() async { | 
|  | Directory dataDir = Directory.fromUri(Platform.script.resolve('data')); | 
|  | await checkTests( | 
|  | dataDir, | 
|  | const RtiNeedDataComputer(), | 
|  | args: args, | 
|  | shardIndex: shardIndex ?? 0, | 
|  | shards: shardIndex != null ? 4 : 1, | 
|  | ); | 
|  | }); | 
|  | } | 
|  |  | 
|  | class Tags { | 
|  | static const String needsTypeArguments = 'needsArgs'; | 
|  | static const String needsSignature = 'needsSignature'; | 
|  | static const String dependencies = 'deps'; | 
|  | static const String explicitTypeCheck = 'explicit'; | 
|  | static const String implicitTypeCheck = 'implicit'; | 
|  | static const String typeArgumentTest = 'test'; | 
|  | static const String typeLiteral = 'exp'; | 
|  | static const String selectors = 'selectors'; | 
|  | static const String instantiationsNeedTypeArguments = 'needsInst'; | 
|  | } | 
|  |  | 
|  | mixin ComputeValueMixin { | 
|  | Compiler get compiler; | 
|  |  | 
|  | KernelFrontendStrategy get frontendStrategy => compiler.frontendStrategy; | 
|  | ResolutionWorldBuilder get resolutionWorldBuilder => | 
|  | compiler.resolutionWorldBuilderForTesting!; | 
|  | RuntimeTypesNeedBuilderImpl get rtiNeedBuilder => | 
|  | frontendStrategy.runtimeTypesNeedBuilderForTesting | 
|  | as RuntimeTypesNeedBuilderImpl; | 
|  | RuntimeTypesNeedImpl get rtiNeed => | 
|  | compiler.backendClosedWorldForTesting!.rtiNeed as RuntimeTypesNeedImpl; | 
|  | ClassEntity? getFrontendClass(ClassEntity cls); | 
|  | MemberEntity? getFrontendMember(MemberEntity member); | 
|  | Local? getFrontendClosure(MemberEntity member); | 
|  |  | 
|  | void findChecks( | 
|  | Features features, | 
|  | String key, | 
|  | Entity? entity, | 
|  | Set<DartType> checks, | 
|  | ) { | 
|  | Set<DartType> types = Set<DartType>(); | 
|  | FindTypeVisitor finder = FindTypeVisitor(entity); | 
|  | for (DartType type in checks) { | 
|  | if (type.accept(finder, null)) { | 
|  | types.add(type); | 
|  | } | 
|  | } | 
|  | List<String> list = types.map(typeToString).toList()..sort(); | 
|  | if (list.isNotEmpty) { | 
|  | features[key] = '[${list.join(',')}]'; | 
|  | } | 
|  | } | 
|  |  | 
|  | void findDependencies(Features features, Entity? entity) { | 
|  | Iterable<Entity> dependencies = entity == null | 
|  | ? const [] | 
|  | : rtiNeedBuilder.typeVariableTestsForTesting! | 
|  | .getTypeArgumentDependencies(entity); | 
|  | if (dependencies.isNotEmpty) { | 
|  | List<String> names = dependencies.map((Entity d) { | 
|  | if (d is MemberEntity && d.enclosingClass != null) { | 
|  | return '${d.enclosingClass!.name}.${d.name}'; | 
|  | } | 
|  | return d.name!; | 
|  | }).toList()..sort(); | 
|  | features[Tags.dependencies] = '[${names.join(',')}]'; | 
|  | } | 
|  | } | 
|  |  | 
|  | String getClassValue(ClassEntity backendClass) { | 
|  | Features features = Features(); | 
|  |  | 
|  | if (rtiNeed.classNeedsTypeArguments(backendClass)) { | 
|  | features.add(Tags.needsTypeArguments); | 
|  | } | 
|  | ClassEntity? frontendClass = getFrontendClass(backendClass); | 
|  | findDependencies(features, frontendClass); | 
|  | if (rtiNeedBuilder.classesUsingTypeVariableLiterals.contains( | 
|  | frontendClass, | 
|  | )) { | 
|  | features.add(Tags.typeLiteral); | 
|  | } | 
|  | if (rtiNeedBuilder.typeVariableTestsForTesting!.classTestsForTesting | 
|  | .contains(frontendClass)) { | 
|  | features.add(Tags.typeArgumentTest); | 
|  | } | 
|  | findChecks( | 
|  | features, | 
|  | Tags.explicitTypeCheck, | 
|  | frontendClass, | 
|  | rtiNeedBuilder.typeVariableTestsForTesting!.explicitIsChecks, | 
|  | ); | 
|  | findChecks( | 
|  | features, | 
|  | Tags.implicitTypeCheck, | 
|  | frontendClass, | 
|  | rtiNeedBuilder.typeVariableTestsForTesting!.implicitIsChecks, | 
|  | ); | 
|  | return features.getText(); | 
|  | } | 
|  |  | 
|  | String getMemberValue(MemberEntity backendMember) { | 
|  | MemberEntity? frontendMember = getFrontendMember(backendMember); | 
|  | Local? frontendClosure = getFrontendClosure(backendMember); | 
|  |  | 
|  | Features features = Features(); | 
|  |  | 
|  | if (backendMember is FunctionEntity) { | 
|  | if (rtiNeed.methodNeedsTypeArguments(backendMember)) { | 
|  | features.add(Tags.needsTypeArguments); | 
|  | } | 
|  | if (rtiNeed.methodNeedsSignature(backendMember)) { | 
|  | features.add(Tags.needsSignature); | 
|  | } | 
|  |  | 
|  | void addFrontendData(Entity entity) { | 
|  | findDependencies(features, entity); | 
|  | if (rtiNeedBuilder.typeVariableTestsForTesting!.methodTestsForTesting | 
|  | .contains(entity)) { | 
|  | features.add(Tags.typeArgumentTest); | 
|  | } | 
|  | findChecks( | 
|  | features, | 
|  | Tags.explicitTypeCheck, | 
|  | entity, | 
|  | rtiNeedBuilder.typeVariableTestsForTesting!.explicitIsChecks, | 
|  | ); | 
|  | findChecks( | 
|  | features, | 
|  | Tags.implicitTypeCheck, | 
|  | entity, | 
|  | rtiNeedBuilder.typeVariableTestsForTesting!.implicitIsChecks, | 
|  | ); | 
|  | rtiNeedBuilder.selectorsNeedingTypeArgumentsForTesting?.forEach(( | 
|  | Selector selector, | 
|  | Set<Entity> targets, | 
|  | ) { | 
|  | if (targets.contains(entity)) { | 
|  | features.addElement(Tags.selectors, selector); | 
|  | } | 
|  | }); | 
|  | rtiNeedBuilder | 
|  | .instantiatedEntitiesNeedingTypeArgumentsForTesting[entity] | 
|  | ?.forEach((GenericInstantiation instantiation) { | 
|  | features.addElement( | 
|  | Tags.instantiationsNeedTypeArguments, | 
|  | instantiation.shortText, | 
|  | ); | 
|  | }); | 
|  | } | 
|  |  | 
|  | if (frontendClosure != null) { | 
|  | addFrontendData(frontendClosure); | 
|  | if (rtiNeedBuilder.localFunctionsUsingTypeVariableLiterals.contains( | 
|  | frontendClosure, | 
|  | )) { | 
|  | features.add(Tags.typeLiteral); | 
|  | } | 
|  | } else if (frontendMember != null) { | 
|  | addFrontendData(frontendMember); | 
|  | if (rtiNeedBuilder.methodsUsingTypeVariableLiterals.contains( | 
|  | frontendMember, | 
|  | )) { | 
|  | features.add(Tags.typeLiteral); | 
|  | } | 
|  | } | 
|  | } | 
|  | return features.getText(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Visitor that determines whether a type refers to [entity]. | 
|  | class FindTypeVisitor extends DartTypeVisitor<bool, Null> { | 
|  | final Entity? entity; | 
|  |  | 
|  | FindTypeVisitor(this.entity); | 
|  |  | 
|  | bool check(DartType type) => visit(type, null); | 
|  |  | 
|  | bool checkList(List<DartType> types) { | 
|  | for (DartType type in types) { | 
|  | if (check(type)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @override | 
|  | bool visitNullableType(NullableType type, _) => visit(type.baseType, null); | 
|  |  | 
|  | @override | 
|  | bool visitNeverType(NeverType type, _) => false; | 
|  |  | 
|  | @override | 
|  | bool visitVoidType(VoidType type, _) => false; | 
|  |  | 
|  | @override | 
|  | bool visitTypeVariableType(TypeVariableType type, _) => | 
|  | type.element.typeDeclaration == entity; | 
|  |  | 
|  | @override | 
|  | bool visitFunctionTypeVariable(FunctionTypeVariable type, _) => false; | 
|  |  | 
|  | @override | 
|  | bool visitFunctionType(FunctionType type, _) => | 
|  | type.returnType.accept(this, null) || | 
|  | checkList(type.typeVariables) || | 
|  | checkList(type.parameterTypes) || | 
|  | checkList(type.optionalParameterTypes) || | 
|  | checkList(type.namedParameterTypes); | 
|  |  | 
|  | @override | 
|  | bool visitInterfaceType(InterfaceType type, _) => | 
|  | type.element == entity || checkList(type.typeArguments); | 
|  |  | 
|  | @override | 
|  | bool visitRecordType(RecordType type, _) => throw UnimplementedError(); | 
|  |  | 
|  | @override | 
|  | bool visitDynamicType(DynamicType type, _) => false; | 
|  |  | 
|  | @override | 
|  | bool visitErasedType(ErasedType type, _) => false; | 
|  |  | 
|  | @override | 
|  | bool visitAnyType(AnyType type, _) => false; | 
|  |  | 
|  | @override | 
|  | bool visitFutureOrType(FutureOrType type, _) => check(type.typeArgument); | 
|  | } | 
|  |  | 
|  | class RtiNeedDataComputer extends DataComputer<String> { | 
|  | const RtiNeedDataComputer(); | 
|  |  | 
|  | /// Compute RTI need data for [member] from the new frontend. | 
|  | /// | 
|  | /// Fills [actualMap] with the data. | 
|  | @override | 
|  | void computeMemberData( | 
|  | Compiler compiler, | 
|  | MemberEntity member, | 
|  | Map<Id, ActualData<String>> actualMap, { | 
|  | bool verbose = false, | 
|  | }) { | 
|  | JClosedWorld closedWorld = compiler.backendClosedWorldForTesting!; | 
|  | JsToElementMap elementMap = closedWorld.elementMap; | 
|  | MemberDefinition definition = elementMap.getMemberDefinition(member); | 
|  | RtiNeedIrComputer( | 
|  | compiler.reporter, | 
|  | actualMap, | 
|  | elementMap, | 
|  | compiler, | 
|  | closedWorld.closureDataLookup, | 
|  | ).run(definition.node); | 
|  | } | 
|  |  | 
|  | /// Compute RTI need data for [cls] from the new frontend. | 
|  | /// | 
|  | /// Fills [actualMap] with the data. | 
|  | @override | 
|  | void computeClassData( | 
|  | Compiler compiler, | 
|  | ClassEntity cls, | 
|  | Map<Id, ActualData<String>> actualMap, { | 
|  | bool verbose = false, | 
|  | }) { | 
|  | JClosedWorld closedWorld = compiler.backendClosedWorldForTesting!; | 
|  | JsToElementMap elementMap = closedWorld.elementMap; | 
|  | RtiNeedIrComputer( | 
|  | compiler.reporter, | 
|  | actualMap, | 
|  | elementMap, | 
|  | compiler, | 
|  | closedWorld.closureDataLookup, | 
|  | ).computeForClass(elementMap.getClassDefinition(cls).node as ir.Class); | 
|  | } | 
|  |  | 
|  | @override | 
|  | DataInterpreter<String> get dataValidator => const StringDataInterpreter(); | 
|  | } | 
|  |  | 
|  | mixin IrMixin implements ComputeValueMixin { | 
|  | @override | 
|  | MemberEntity? getFrontendMember(MemberEntity backendMember) { | 
|  | ElementEnvironment elementEnvironment = | 
|  | compiler.frontendClosedWorldForTesting!.elementEnvironment; | 
|  | LibraryEntity frontendLibrary = elementEnvironment.lookupLibrary( | 
|  | backendMember.library.canonicalUri, | 
|  | )!; | 
|  | if (backendMember.enclosingClass != null) { | 
|  | if (backendMember.enclosingClass!.isClosure) return null; | 
|  | ClassEntity frontendClass = elementEnvironment.lookupClass( | 
|  | frontendLibrary, | 
|  | backendMember.enclosingClass!.name, | 
|  | )!; | 
|  | if (backendMember is ConstructorEntity) { | 
|  | return elementEnvironment.lookupConstructor( | 
|  | frontendClass, | 
|  | backendMember.name!, | 
|  | ); | 
|  | } else { | 
|  | return elementEnvironment.lookupClassMember( | 
|  | frontendClass, | 
|  | Name( | 
|  | backendMember.name!, | 
|  | frontendClass.library.canonicalUri, | 
|  | isSetter: backendMember.isSetter, | 
|  | ), | 
|  | ); | 
|  | } | 
|  | } | 
|  | return elementEnvironment.lookupLibraryMember( | 
|  | frontendLibrary, | 
|  | backendMember.name!, | 
|  | setter: backendMember.isSetter, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @override | 
|  | ClassEntity? getFrontendClass(ClassEntity backendClass) { | 
|  | if (backendClass.isClosure) return null; | 
|  | ElementEnvironment elementEnvironment = | 
|  | compiler.frontendClosedWorldForTesting!.elementEnvironment; | 
|  | LibraryEntity frontendLibrary = elementEnvironment.lookupLibrary( | 
|  | backendClass.library.canonicalUri, | 
|  | )!; | 
|  | return elementEnvironment.lookupClass(frontendLibrary, backendClass.name); | 
|  | } | 
|  |  | 
|  | @override | 
|  | Local? getFrontendClosure(MemberEntity member) { | 
|  | JClosedWorld closedWorld = compiler.backendClosedWorldForTesting!; | 
|  | ir.Node node = closedWorld.elementMap.getMemberDefinition(member).node; | 
|  | if (node is ir.FunctionDeclaration || node is ir.FunctionExpression) { | 
|  | KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy; | 
|  | KernelToElementMap frontendElementMap = frontendStrategy.elementMap; | 
|  | return frontendElementMap.getLocalFunction(node as ir.LocalFunction); | 
|  | } | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | class RtiClassNeedIrComputer | 
|  | with | 
|  | DataRegistry<String>, | 
|  | ComputeValueMixin, | 
|  | IrMixin, | 
|  | IrDataRegistryMixin<String> { | 
|  | @override | 
|  | final Compiler compiler; | 
|  | final JsToElementMap _elementMap; | 
|  | @override | 
|  | final Map<Id, ActualData<String>> actualMap; | 
|  |  | 
|  | RtiClassNeedIrComputer(this.compiler, this._elementMap, this.actualMap); | 
|  |  | 
|  | @override | 
|  | DiagnosticReporter get reporter => compiler.reporter; | 
|  |  | 
|  | void computeClassValue(ClassEntity cls) { | 
|  | Id id = ClassId(cls.name); | 
|  | final node = _elementMap.getClassDefinition(cls).node as ir.TreeNode; | 
|  | ir.TreeNode nodeWithOffset = computeTreeNodeWithOffset(node)!; | 
|  | registerValue( | 
|  | nodeWithOffset.location!.file, | 
|  | nodeWithOffset.fileOffset, | 
|  | id, | 
|  | getClassValue(cls), | 
|  | cls, | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// AST visitor for computing inference data for a member. | 
|  | class RtiNeedIrComputer extends IrDataExtractor<String> | 
|  | with ComputeValueMixin, IrMixin { | 
|  | final JsToElementMap _elementMap; | 
|  | final ClosureData _closureDataLookup; | 
|  | @override | 
|  | final Compiler compiler; | 
|  |  | 
|  | RtiNeedIrComputer( | 
|  | DiagnosticReporter reporter, | 
|  | Map<Id, ActualData<String>> actualMap, | 
|  | this._elementMap, | 
|  | this.compiler, | 
|  | this._closureDataLookup, | 
|  | ) : super(reporter, actualMap); | 
|  |  | 
|  | @override | 
|  | String computeClassValue(Id id, ir.Class node) { | 
|  | return getClassValue(_elementMap.getClass(node)); | 
|  | } | 
|  |  | 
|  | @override | 
|  | String computeMemberValue(Id id, ir.Member node) { | 
|  | return getMemberValue(_elementMap.getMember(node)); | 
|  | } | 
|  |  | 
|  | @override | 
|  | String? computeNodeValue(Id id, ir.TreeNode node) { | 
|  | if (node is ir.FunctionExpression || node is ir.FunctionDeclaration) { | 
|  | ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo( | 
|  | node as ir.LocalFunction, | 
|  | ); | 
|  | return getMemberValue(info.callMethod!); | 
|  | } | 
|  | return null; | 
|  | } | 
|  | } |