| // Copyright (c) 2017, 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:js_shared/synced/embedded_names.dart' show JsGetName; | |
| import 'package:js_shared/variance.dart'; | |
| import 'package:kernel/ast.dart' as ir; | |
| import 'package:kernel/class_hierarchy.dart' as ir; | |
| import 'package:kernel/core_types.dart' as ir; | |
| // ignore: implementation_imports | |
| import 'package:kernel/src/bounds_checks.dart' as ir; | |
| import 'package:kernel/text/debug_printer.dart'; | |
| import 'package:kernel/type_environment.dart' as ir; | |
| import '../common.dart'; | |
| import '../common/elements.dart'; | |
| import '../common/names.dart'; | |
| import '../constants/values.dart'; | |
| import '../elements/entities.dart'; | |
| import '../elements/entity_map.dart'; | |
| import '../elements/names.dart'; | |
| import '../elements/types.dart'; | |
| import '../ir/annotations.dart'; | |
| import '../ir/element_map.dart'; | |
| import '../ir/impact.dart'; | |
| import '../ir/impact_data.dart'; | |
| import '../ir/types.dart'; | |
| import '../ir/util.dart'; | |
| import '../ir/visitors.dart'; | |
| import '../js/js.dart' as js; | |
| import '../js_backend/annotations.dart'; | |
| import '../js_backend/backend_impact.dart'; | |
| import '../js_backend/backend_usage.dart'; | |
| import '../js_backend/custom_elements_analysis.dart'; | |
| import '../js_backend/namer.dart'; | |
| import '../js_backend/native_data.dart'; | |
| import '../js_backend/runtime_types_resolution.dart'; | |
| import '../js_model/elements.dart'; | |
| import '../js_model/locals.dart'; | |
| import '../native/behavior.dart'; | |
| import '../native/enqueue.dart'; | |
| import '../options.dart'; | |
| import '../ordered_typeset.dart'; | |
| import '../universe/call_structure.dart'; | |
| import '../universe/selector.dart'; | |
| import '../universe/world_impact.dart'; | |
| import 'dart2js_target.dart'; | |
| import 'element_map.dart'; | |
| import 'env.dart'; | |
| import 'kernel_impact.dart'; | |
| import 'transformations/modular/late_lowering.dart' | |
| as late_lowering | |
| show | |
| isBackingFieldForLateInstanceField, | |
| isBackingFieldForLateFinalInstanceField; | |
| /// Implementation of [IrToElementMap] that only supports world | |
| /// impact computation. | |
| class KernelToElementMap implements IrToElementMap { | |
| final CompilerOptions options; | |
| @override | |
| final DiagnosticReporter reporter; | |
| final NativeBasicDataBuilder nativeBasicDataBuilder = | |
| NativeBasicDataBuilder(); | |
| NativeBasicData? _nativeBasicData; | |
| late final KCommonElements _commonElements; | |
| late final KernelElementEnvironment _elementEnvironment; | |
| late final DartTypeConverter _typeConverter; | |
| late final KernelDartTypes _types; | |
| ir.CoreTypes? _coreTypes; | |
| ir.TypeEnvironment? _typeEnvironment; | |
| ir.ClassHierarchy? _classHierarchy; | |
| late final ConstantValuefier _constantValuefier; | |
| /// Library environment. Used for fast lookup. | |
| KProgramEnv env = KProgramEnv(); | |
| /// TODO(natebiggs): Align J- and K- names here. We only have J- entities now | |
| /// so we should drop the J- prefix. | |
| final EntityDataEnvMap<JLibrary, KLibraryData, KLibraryEnv> libraries = | |
| EntityDataEnvMap<JLibrary, KLibraryData, KLibraryEnv>(); | |
| final EntityDataEnvMap<JClass, KClassData, KClassEnv> classes = | |
| EntityDataEnvMap<JClass, KClassData, KClassEnv>(); | |
| final EntityDataMap<JMember, KMemberData> members = | |
| EntityDataMap<JMember, KMemberData>(); | |
| final EntityDataMap<JTypeVariable, KTypeVariableData> typeVariables = | |
| EntityDataMap<JTypeVariable, KTypeVariableData>(); | |
| /// Set to `true` before creating the J-World from the K-World to assert that | |
| /// no entities are created late. | |
| bool envIsClosed = false; | |
| final Map<ir.Library, JLibrary> libraryMap = {}; | |
| final Map<ir.Class, JClass> classMap = {}; | |
| /// Map from [ir.TypeParameter] nodes to the corresponding | |
| /// [TypeVariableEntity]. | |
| /// | |
| /// Normally the type variables are [JTypeVariable]s, but for type | |
| /// parameters on local function (in the frontend) these are _not_ since | |
| /// their type declaration is neither a class nor a member. In the backend, | |
| /// these type parameters belong to the call-method and are therefore indexed. | |
| final Map<ir.TypeParameter, TypeVariableEntity> typeVariableMap = {}; | |
| final Map<ir.Member, JConstructor> constructorMap = {}; | |
| final Map<ir.Procedure, JFunction> methodMap = {}; | |
| final Map<ir.Field, JField> fieldMap = {}; | |
| final Map<ir.TreeNode, Local> localFunctionMap = {}; | |
| BehaviorBuilder? _nativeBehaviorBuilder; | |
| Map<ir.Member, ImpactData>? impactDataForTesting; | |
| KernelToElementMap(this.reporter, this.options) { | |
| _elementEnvironment = KernelElementEnvironment(this); | |
| _typeConverter = DartTypeConverter(this); | |
| _types = KernelDartTypes(this); | |
| _commonElements = KCommonElements(_types, _elementEnvironment); | |
| _constantValuefier = ConstantValuefier(this); | |
| } | |
| /// Access to the [DartTypes] object. | |
| DartTypes get types => _types; | |
| KernelElementEnvironment get elementEnvironment => _elementEnvironment; | |
| /// Access to the commonly used elements and types. | |
| @override | |
| KCommonElements get commonElements => _commonElements; | |
| FunctionEntity? get _mainFunction { | |
| return env.mainMethod != null | |
| ? getMethodInternal(env.mainMethod as ir.Procedure) | |
| : null; | |
| } | |
| LibraryEntity? get _mainLibrary { | |
| return env.mainMethod != null | |
| ? getLibraryInternal(env.mainMethod!.enclosingLibrary) | |
| : null; | |
| } | |
| SourceSpan getSourceSpan(Spannable spannable, Entity? currentElement) { | |
| SourceSpan fromSpannable(Spannable spannable) { | |
| if (spannable is JLibrary) { | |
| KLibraryEnv env = libraries.getEnv(spannable); | |
| return computeSourceSpanFromTreeNode(env.library); | |
| } else if (spannable is JClass) { | |
| KClassData data = classes.getData(spannable); | |
| return computeSourceSpanFromTreeNode(data.node); | |
| } else if (spannable is JMember) { | |
| KMemberData data = members.getData(spannable); | |
| return computeSourceSpanFromTreeNode(data.node); | |
| } else if (spannable is JLocalFunction) { | |
| return getSourceSpan(spannable.memberContext, currentElement); | |
| } else if (spannable is JLocal) { | |
| return getSourceSpan(spannable.memberContext, currentElement); | |
| } | |
| return SourceSpan.unknown(); | |
| } | |
| SourceSpan sourceSpan = fromSpannable(spannable); | |
| if (sourceSpan.isKnown) return sourceSpan; | |
| return fromSpannable(currentElement!); | |
| } | |
| LibraryEntity? lookupLibrary(Uri uri) { | |
| KLibraryEnv? libraryEnv = env.lookupLibrary(uri); | |
| if (libraryEnv == null) return null; | |
| return getLibraryInternal(libraryEnv.library, libraryEnv); | |
| } | |
| String _getLibraryName(JLibrary library) { | |
| KLibraryEnv libraryEnv = libraries.getEnv(library); | |
| return libraryEnv.library.name ?? ''; | |
| } | |
| MemberEntity? lookupLibraryMember( | |
| JLibrary library, | |
| String name, { | |
| bool setter = false, | |
| }) { | |
| KLibraryEnv libraryEnv = libraries.getEnv(library); | |
| ir.Member? member = libraryEnv.lookupMember(name, setter: setter); | |
| return member != null ? getMember(member) : null; | |
| } | |
| void _forEachLibraryMember( | |
| JLibrary library, | |
| void Function(MemberEntity member) f, | |
| ) { | |
| KLibraryEnv libraryEnv = libraries.getEnv(library); | |
| libraryEnv.forEachMember((ir.Member node) { | |
| f(getMember(node)); | |
| }); | |
| } | |
| ClassEntity? lookupClass(JLibrary library, String name) { | |
| KLibraryEnv libraryEnv = libraries.getEnv(library); | |
| KClassEnv? classEnv = libraryEnv.lookupClass(name); | |
| if (classEnv != null) { | |
| return getClassInternal(classEnv.cls, classEnv); | |
| } | |
| return null; | |
| } | |
| void _forEachClass(JLibrary library, void Function(ClassEntity cls) f) { | |
| KLibraryEnv libraryEnv = libraries.getEnv(library); | |
| libraryEnv.forEachClass((KClassEnv classEnv) { | |
| if (!classEnv.isUnnamedMixinApplication) { | |
| f(getClassInternal(classEnv.cls, classEnv)); | |
| } | |
| }); | |
| } | |
| /// Returns the [ClassEntity] for [node] while ensuring that the member | |
| /// environment for [node] is computed. | |
| /// | |
| /// This is needed to ensure that live members are always included in the | |
| /// environment of a class. Static members and mixed in members a member | |
| /// can be become live through static access and mixin application, | |
| /// respectively, which does not require lookup into the class members. | |
| /// | |
| /// Since the J-model class environment is computed from the K-model | |
| /// environment, not ensuring the computation of the class members, can result | |
| /// in a live member being present in the J-model but unavailable when queried | |
| /// as a member of its enclosing class. | |
| JClass getClassForMemberInternal(ir.Class node) { | |
| final cls = getClassInternal(node); | |
| classes.getEnv(cls).ensureMembers(this); | |
| return cls; | |
| } | |
| MemberEntity? lookupClassMember( | |
| JClass cls, | |
| Name name, { | |
| bool setter = false, | |
| }) { | |
| KClassEnv classEnv = classes.getEnv(cls); | |
| return classEnv.lookupMember(this, name); | |
| } | |
| ConstructorEntity? lookupConstructor(JClass cls, String name) { | |
| KClassEnv classEnv = classes.getEnv(cls); | |
| return classEnv.lookupConstructor(this, name); | |
| } | |
| /// Return the [InterfaceType] corresponding to the [cls] with the given | |
| /// [typeArguments] and [nullability]. | |
| @override | |
| InterfaceType createInterfaceType( | |
| ir.Class cls, | |
| List<ir.DartType> typeArguments, | |
| ) { | |
| return types.interfaceType(getClass(cls), getDartTypes(typeArguments)); | |
| } | |
| LibraryEntity getLibrary(ir.Library node) => getLibraryInternal(node); | |
| /// Returns the [ClassEntity] corresponding to the class [node]. | |
| @override | |
| ClassEntity getClass(ir.Class node) => getClassInternal(node); | |
| @override | |
| InterfaceType? getSuperType(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureSupertypes(cls, data); | |
| return data.supertype; | |
| } | |
| /// Returns the superclass of [cls] if any. | |
| ClassEntity? getSuperClass(ClassEntity cls) { | |
| return getSuperType(cls as JClass)?.element; | |
| } | |
| void _ensureCallType(ClassEntity cls, KClassData data) { | |
| if (!data.isCallTypeComputed) { | |
| MemberEntity? callMember = _elementEnvironment.lookupClassMember( | |
| cls, | |
| Names.call, | |
| ); | |
| if (callMember is FunctionEntity && | |
| callMember.isFunction && | |
| !callMember.isAbstract) { | |
| data.callType = _elementEnvironment.getFunctionType(callMember); | |
| } | |
| data.isCallTypeComputed = true; | |
| } | |
| } | |
| void _ensureThisAndRawType(ClassEntity cls, KClassData data) { | |
| if (data.thisType == null) { | |
| ir.Class node = data.node; | |
| if (node.typeParameters.isEmpty) { | |
| data.thisType = data.rawType = types.interfaceType( | |
| cls, | |
| const <DartType>[], | |
| ); | |
| } else { | |
| data.thisType = types.interfaceType( | |
| cls, | |
| List<DartType>.generate(node.typeParameters.length, (int index) { | |
| return types.typeVariableType( | |
| getTypeVariableInternal(node.typeParameters[index]), | |
| ); | |
| }), | |
| ); | |
| data.rawType = types.interfaceType( | |
| cls, | |
| List<DartType>.filled( | |
| node.typeParameters.length, | |
| types.dynamicType(), | |
| ), | |
| ); | |
| } | |
| } | |
| } | |
| void _ensureJsInteropType(ClassEntity cls, KClassData data) { | |
| if (data.jsInteropType == null) { | |
| ir.Class node = data.node; | |
| if (node.typeParameters.isEmpty) { | |
| _ensureThisAndRawType(cls, data); | |
| data.jsInteropType = data.thisType; | |
| } else { | |
| data.jsInteropType = types.interfaceType( | |
| cls, | |
| List<DartType>.filled(node.typeParameters.length, types.anyType()), | |
| ); | |
| } | |
| } | |
| } | |
| void _ensureClassInstantiationToBounds(ClassEntity cls, KClassData data) { | |
| if (data.instantiationToBounds == null) { | |
| ir.Class node = data.node; | |
| if (node.typeParameters.isEmpty) { | |
| _ensureThisAndRawType(cls, data); | |
| data.instantiationToBounds = data.thisType; | |
| } else { | |
| data.instantiationToBounds = getInterfaceType( | |
| ir.instantiateToBounds( | |
| coreTypes.nonNullableRawType(node), | |
| coreTypes.objectClass, | |
| ) as ir.InterfaceType, | |
| ); | |
| } | |
| } | |
| } | |
| @override | |
| TypeVariableEntity getTypeVariable(ir.TypeParameter node) => | |
| getTypeVariableInternal(node); | |
| void _ensureSupertypes(ClassEntity cls, KClassData data) { | |
| if (data.orderedTypeSet == null) { | |
| _ensureThisAndRawType(cls, data); | |
| ir.Class node = data.node; | |
| if (node.supertype == null) { | |
| data.orderedTypeSet = OrderedTypeSet.singleton(data.thisType!); | |
| data.isMixinApplication = false; | |
| data.interfaces = const <InterfaceType>[]; | |
| } else { | |
| // Set of canonical supertypes. | |
| // | |
| // This is necessary to support when a class implements the same | |
| // supertype in multiple non-conflicting ways, like implementing A<int*> | |
| // and A<int?> or B<Object?> and B<dynamic>. | |
| Set<InterfaceType> canonicalSupertypes = <InterfaceType>{}; | |
| InterfaceType processSupertype(ir.Supertype supertypeNode) { | |
| supertypeNode = classHierarchy.getClassAsInstanceOf( | |
| node, | |
| supertypeNode.classNode, | |
| )!; | |
| InterfaceType supertype = _typeConverter.visitSupertype( | |
| supertypeNode, | |
| ); | |
| canonicalSupertypes.add(supertype); | |
| JClass superclass = supertype.element as JClass; | |
| KClassData superdata = classes.getData(superclass); | |
| _ensureSupertypes(superclass, superdata); | |
| for (InterfaceType supertype | |
| in superdata.orderedTypeSet!.supertypes!) { | |
| ir.Supertype? canonicalSupertype = classHierarchy | |
| .getClassAsInstanceOf( | |
| node, | |
| getClassNode(supertype.element as JClass), | |
| ); | |
| if (canonicalSupertype != null) { | |
| supertype = _typeConverter.visitSupertype(canonicalSupertype); | |
| } else { | |
| assert( | |
| supertype.typeArguments.isEmpty, | |
| "Generic synthetic supertypes are not supported", | |
| ); | |
| } | |
| canonicalSupertypes.add(supertype); | |
| } | |
| return supertype; | |
| } | |
| InterfaceType supertype; | |
| List<InterfaceType> interfaces = <InterfaceType>[]; | |
| if (node.isMixinDeclaration) { | |
| // A mixin declaration | |
| // | |
| // mixin M on A, B, C {} | |
| // | |
| // is encoded by CFE as | |
| // | |
| // abstract class M extends A implements B, C {} | |
| // abstract class M extends A&B&C {} | |
| // | |
| // but we encode it as | |
| // | |
| // abstract class M extends Object implements A, B, C {} | |
| // | |
| // so we need get the superclasses from the on-clause, A, B, and C, | |
| // through [superclassConstraints]. | |
| for (ir.Supertype constraint in node.onClause) { | |
| interfaces.add(processSupertype(constraint)); | |
| } | |
| // Set superclass to `Object`. | |
| supertype = _commonElements.objectType; | |
| } else { | |
| supertype = processSupertype(node.supertype!); | |
| } | |
| if (supertype == _commonElements.objectType) { | |
| ClassEntity defaultSuperclass = _commonElements.getDefaultSuperclass( | |
| cls, | |
| nativeBasicData, | |
| ); | |
| data.supertype = _elementEnvironment.getRawType(defaultSuperclass); | |
| assert( | |
| data.supertype!.typeArguments.isEmpty, | |
| "Generic default supertypes are not supported", | |
| ); | |
| canonicalSupertypes.add(data.supertype!); | |
| } else { | |
| data.supertype = supertype; | |
| } | |
| if (node.mixedInType != null) { | |
| data.isMixinApplication = true; | |
| interfaces.add( | |
| data.mixedInType = processSupertype(node.mixedInType!), | |
| ); | |
| } else { | |
| data.isMixinApplication = false; | |
| } | |
| for (var supertype in node.implementedTypes) { | |
| interfaces.add(processSupertype(supertype)); | |
| } | |
| OrderedTypeSetBuilder setBuilder = KernelOrderedTypeSetBuilder( | |
| this, | |
| cls, | |
| ); | |
| data.orderedTypeSet = setBuilder.createOrderedTypeSet( | |
| canonicalSupertypes, | |
| ); | |
| data.interfaces = interfaces; | |
| } | |
| } | |
| } | |
| /// Returns the [MemberEntity] corresponding to the member [node]. | |
| @override | |
| MemberEntity getMember(ir.Member node) { | |
| if (node is ir.Field) { | |
| return getFieldInternal(node); | |
| } else if (node is ir.Constructor) { | |
| return getConstructorInternal(node); | |
| } else if (node is ir.Procedure) { | |
| if (node.kind == ir.ProcedureKind.Factory) { | |
| return getConstructorInternal(node); | |
| } else { | |
| return getMethodInternal(node); | |
| } | |
| } | |
| throw UnsupportedError("Unexpected member: $node"); | |
| } | |
| /// Returns the [ConstructorEntity] corresponding to the generative or factory | |
| /// constructor [node]. | |
| @override | |
| ConstructorEntity getConstructor(ir.Member node) => | |
| getConstructorInternal(node); | |
| /// Returns the [ConstructorEntity] corresponding to a super initializer in | |
| /// [constructor]. | |
| /// | |
| /// The IR resolves super initializers to a [target] up in the type hierarchy. | |
| /// Most of the time, the result of this function will be the entity | |
| /// corresponding to that target. In the presence of unnamed mixins, this | |
| /// function returns an entity for an intermediate synthetic constructor that | |
| /// kernel doesn't explicitly represent. | |
| /// | |
| /// For example: | |
| /// class M {} | |
| /// class C extends Object with M {} | |
| /// | |
| /// Kernel will say that C()'s super initializer resolves to Object(), but | |
| /// this function will return an entity representing the unnamed mixin | |
| /// application "Object+M"'s constructor. | |
| ConstructorEntity getSuperConstructor( | |
| ir.Constructor sourceNode, | |
| ir.Member targetNode, | |
| ) { | |
| ConstructorEntity source = getConstructor(sourceNode); | |
| ClassEntity sourceClass = source.enclosingClass; | |
| ConstructorEntity target = getConstructor(targetNode); | |
| ClassEntity targetClass = target.enclosingClass; | |
| JClass? superClass = | |
| getSuperType(sourceClass as JClass)?.element as JClass?; | |
| if (superClass == targetClass) { | |
| return target; | |
| } | |
| /// This path is needed for synthetically injected superclasses like | |
| /// `Interceptor` and `LegacyJavaScriptObject`. | |
| KClassEnv env = classes.getEnv(superClass!); | |
| ConstructorEntity? constructor = env.lookupConstructor(this, target.name); | |
| if (constructor != null) { | |
| return constructor; | |
| } | |
| throw failedAt(source, "Super constructor for $source not found."); | |
| } | |
| /// Returns the [FunctionEntity] corresponding to the procedure [node]. | |
| @override | |
| FunctionEntity getMethod(ir.Procedure node) => getMethodInternal(node); | |
| /// Returns the [FieldEntity] corresponding to the field [node]. | |
| @override | |
| FieldEntity getField(ir.Field node) => getFieldInternal(node); | |
| /// Returns the [DartType] corresponding to [type]. | |
| @override | |
| DartType getDartType(ir.DartType type) => _typeConverter.visitType(type); | |
| /// Returns the [TypeVariableType] corresponding to [type]. | |
| TypeVariableType getTypeVariableType(ir.TypeParameterType type) => | |
| getDartType(type).withoutNullability as TypeVariableType; | |
| List<DartType> getDartTypes(List<ir.DartType> types) { | |
| List<DartType> list = <DartType>[]; | |
| for (var type in types) { | |
| list.add(getDartType(type)); | |
| } | |
| return list; | |
| } | |
| /// Returns the [InterfaceType] corresponding to [type]. | |
| InterfaceType getInterfaceType(ir.InterfaceType type) => | |
| _typeConverter.visitType(type).withoutNullability as InterfaceType; | |
| /// Returns the [FunctionType] of the [node]. | |
| @override | |
| FunctionType getFunctionType(ir.FunctionNode node) { | |
| DartType returnType; | |
| if (node.parent is ir.Constructor) { | |
| // The return type on generative constructors is `void`, but we need | |
| // `dynamic` type to match the element model. | |
| returnType = types.dynamicType(); | |
| } else { | |
| returnType = getDartType(node.returnType); | |
| } | |
| List<DartType> parameterTypes = <DartType>[]; | |
| List<DartType> optionalParameterTypes = <DartType>[]; | |
| DartType getParameterType(ir.Variable variable) { | |
| // isCovariant implies this FunctionNode is a class Procedure. | |
| var isCovariant = | |
| variable.isCovariantByDeclaration || variable.isCovariantByClass; | |
| return types.getTearOffParameterType( | |
| getDartType(variable.type), | |
| isCovariant, | |
| ); | |
| } | |
| for (ir.PositionalParameter parameter in node.positionalParameters) { | |
| if (parameterTypes.length == node.requiredParameterCount) { | |
| optionalParameterTypes.add(getParameterType(parameter)); | |
| } else { | |
| parameterTypes.add(getParameterType(parameter)); | |
| } | |
| } | |
| List<String> namedParameters = <String>[]; | |
| Set<String> requiredNamedParameters = <String>{}; | |
| List<DartType> namedParameterTypes = <DartType>[]; | |
| List<ir.NamedParameter> sortedNamedParameters = | |
| node.namedParameters.toList() | |
| ..sort((a, b) => a.parameterName.compareTo(b.parameterName)); | |
| for (ir.NamedParameter parameter in sortedNamedParameters) { | |
| namedParameters.add(parameter.parameterName); | |
| namedParameterTypes.add(getParameterType(parameter)); | |
| if (parameter.isRequired) { | |
| requiredNamedParameters.add(parameter.parameterName); | |
| } | |
| } | |
| List<FunctionTypeVariable> typeVariables; | |
| if (node.typeParameters.isNotEmpty) { | |
| List<DartType> typeParameters = <DartType>[]; | |
| for (ir.TypeParameter typeParameter in node.typeParameters) { | |
| typeParameters.add( | |
| getDartType( | |
| ir.TypeParameterType(typeParameter, ir.Nullability.nonNullable), | |
| ), | |
| ); | |
| } | |
| typeVariables = List<FunctionTypeVariable>.generate( | |
| node.typeParameters.length, | |
| (int index) => types.functionTypeVariable(index), | |
| ); | |
| DartType subst(DartType type) { | |
| return types.subst(typeVariables, typeParameters, type); | |
| } | |
| returnType = subst(returnType); | |
| parameterTypes = parameterTypes.map(subst).toList(); | |
| optionalParameterTypes = optionalParameterTypes.map(subst).toList(); | |
| namedParameterTypes = namedParameterTypes.map(subst).toList(); | |
| for (int index = 0; index < typeVariables.length; index++) { | |
| typeVariables[index].bound = subst( | |
| getDartType(node.typeParameters[index].bound), | |
| ); | |
| } | |
| } else { | |
| typeVariables = const <FunctionTypeVariable>[]; | |
| } | |
| return types.functionType( | |
| returnType, | |
| parameterTypes, | |
| optionalParameterTypes, | |
| namedParameters, | |
| requiredNamedParameters, | |
| namedParameterTypes, | |
| typeVariables, | |
| ); | |
| } | |
| @override | |
| DartType substByContext(DartType type, InterfaceType context) { | |
| return types.subst( | |
| context.typeArguments, | |
| getThisType(context.element as JClass).typeArguments, | |
| type, | |
| ); | |
| } | |
| /// Returns the type of the `call` method on 'type'. | |
| /// | |
| /// If [type] doesn't have a `call` member or has a non-method `call` member, | |
| /// `null` is returned. | |
| @override | |
| FunctionType? getCallType(InterfaceType type) { | |
| JClass cls = type.element as JClass; | |
| KClassData data = classes.getData(cls); | |
| _ensureCallType(cls, data); | |
| if (data.callType != null) { | |
| return substByContext(data.callType!, type) as FunctionType?; | |
| } | |
| return null; | |
| } | |
| @override | |
| InterfaceType getThisType(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureThisAndRawType(cls, data); | |
| return data.thisType!; | |
| } | |
| InterfaceType? _getJsInteropType(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureJsInteropType(cls, data); | |
| return data.jsInteropType; | |
| } | |
| InterfaceType _getRawType(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureThisAndRawType(cls, data); | |
| return data.rawType!; | |
| } | |
| InterfaceType _getClassInstantiationToBounds(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureClassInstantiationToBounds(cls, data); | |
| return data.instantiationToBounds!; | |
| } | |
| DartType _getFieldType(JField field) { | |
| KFieldData data = members.getData(field) as KFieldData; | |
| return data.getFieldType(this); | |
| } | |
| FunctionType _getFunctionType(JFunction function) { | |
| KFunctionData data = members.getData(function) as KFunctionData; | |
| return data.getFunctionType(this); | |
| } | |
| List<TypeVariableType> _getFunctionTypeVariables(JFunction function) { | |
| KFunctionData data = members.getData(function) as KFunctionData; | |
| return data.getFunctionTypeVariables(this); | |
| } | |
| @override | |
| DartType getTypeVariableBound(JTypeVariable typeVariable) { | |
| KTypeVariableData data = typeVariables.getData(typeVariable); | |
| return data.getBound(this); | |
| } | |
| @override | |
| List<Variance> getTypeVariableVariances(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| return data.getVariances(); | |
| } | |
| /// Returns the class mixed into [cls] if any. | |
| // TODO(johnniwinther): Replace this by a `getAppliedMixins` function that | |
| // return transitively mixed in classes like in: | |
| // class A {} | |
| // class B = Object with A; | |
| // class C = Object with B; | |
| ClassEntity? getAppliedMixin(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureSupertypes(cls, data); | |
| return data.mixedInType?.element; | |
| } | |
| bool _isMixinApplication(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureSupertypes(cls, data); | |
| return data.isMixinApplication; | |
| } | |
| bool _isUnnamedMixinApplication(JClass cls) { | |
| KClassEnv env = classes.getEnv(cls); | |
| return env.isUnnamedMixinApplication; | |
| } | |
| void _forEachSupertype(JClass cls, void Function(InterfaceType supertype) f) { | |
| KClassData data = classes.getData(cls); | |
| _ensureSupertypes(cls, data); | |
| data.orderedTypeSet!.supertypes!.forEach(f); | |
| } | |
| void _forEachMixin(JClass? cls, void Function(ClassEntity mixin) f) { | |
| while (cls != null) { | |
| KClassData data = classes.getData(cls); | |
| _ensureSupertypes(cls, data); | |
| if (data.mixedInType != null) { | |
| f(data.mixedInType!.element); | |
| } | |
| cls = data.supertype?.element as JClass?; | |
| } | |
| } | |
| void _forEachConstructor( | |
| JClass cls, | |
| void Function(ConstructorEntity member) f, | |
| ) { | |
| KClassEnv env = classes.getEnv(cls); | |
| env.forEachConstructor(this, f); | |
| } | |
| void _forEachLocalClassMember( | |
| JClass cls, | |
| void Function(MemberEntity member) f, | |
| ) { | |
| KClassEnv env = classes.getEnv(cls); | |
| env.forEachMember(this, (MemberEntity member) { | |
| f(member); | |
| }); | |
| } | |
| void forEachInjectedClassMember( | |
| JClass cls, | |
| void Function(MemberEntity member) f, | |
| ) { | |
| throw UnsupportedError( | |
| 'KernelToElementMapBase._forEachInjectedClassMember', | |
| ); | |
| } | |
| void _forEachClassMember( | |
| JClass cls, | |
| void Function(ClassEntity cls, MemberEntity member) f, | |
| ) { | |
| KClassEnv env = classes.getEnv(cls); | |
| env.forEachMember(this, (MemberEntity member) { | |
| f(cls, member); | |
| }); | |
| KClassData data = classes.getData(cls); | |
| _ensureSupertypes(cls, data); | |
| if (data.supertype != null) { | |
| _forEachClassMember(data.supertype!.element as JClass, f); | |
| } | |
| } | |
| @override | |
| InterfaceType? asInstanceOf(InterfaceType type, ClassEntity cls) { | |
| OrderedTypeSet orderedTypeSet = getOrderedTypeSet(type.element as JClass); | |
| InterfaceType? supertype = orderedTypeSet.asInstanceOf( | |
| cls, | |
| getHierarchyDepth(cls as JClass), | |
| ); | |
| if (supertype != null) { | |
| supertype = substByContext(supertype, type) as InterfaceType?; | |
| } | |
| return supertype; | |
| } | |
| @override | |
| OrderedTypeSet getOrderedTypeSet(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureSupertypes(cls, data); | |
| return data.orderedTypeSet!; | |
| } | |
| /// Returns all supertypes of [cls]. | |
| Iterable<InterfaceType> getSuperTypes(ClassEntity cls) { | |
| return getOrderedTypeSet(cls as JClass).supertypes!; | |
| } | |
| /// Returns the hierarchy depth of [cls]. | |
| @override | |
| int getHierarchyDepth(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureSupertypes(cls, data); | |
| return data.orderedTypeSet!.maxDepth; | |
| } | |
| @override | |
| Iterable<InterfaceType> getInterfaces(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| _ensureSupertypes(cls, data); | |
| assert(data.interfaces != null); | |
| return data.interfaces!; | |
| } | |
| /// Returns the defining node for [member]. | |
| ir.Member getMemberNode(MemberEntity member) { | |
| return members.getData(member as JMember).node; | |
| } | |
| /// Returns the defining node for [cls]. | |
| ir.Class getClassNode(ClassEntity cls) { | |
| return classes.getData(cls as JClass).node; | |
| } | |
| /// Return the [ImportEntity] corresponding to [node]. | |
| ImportEntity? getImport(ir.LibraryDependency? node) { | |
| if (node == null) return null; | |
| ir.Library library = node.enclosingLibrary; | |
| KLibraryData data = libraries.getData(getLibraryInternal(library)); | |
| return data.imports![node]; | |
| } | |
| /// Returns the core types for the underlying kernel model. | |
| @override | |
| ir.CoreTypes get coreTypes => _coreTypes ??= ir.CoreTypes(env.mainComponent); | |
| /// Returns the type environment for the underlying kernel model. | |
| ir.TypeEnvironment get typeEnvironment => | |
| _typeEnvironment ??= ir.TypeEnvironment(coreTypes, classHierarchy); | |
| /// Returns the class hierarchy for the underlying kernel model. | |
| ir.ClassHierarchy get classHierarchy => | |
| _classHierarchy ??= ir.ClassHierarchy(env.mainComponent, coreTypes); | |
| @override | |
| Name getName(ir.Name name, {bool setter = false}) { | |
| return Name( | |
| name.text, | |
| name.isPrivate ? name.library!.importUri : null, | |
| isSetter: setter, | |
| ); | |
| } | |
| /// Returns the [CallStructure] corresponding to the [arguments]. | |
| @override | |
| CallStructure getCallStructure(ir.Arguments arguments) { | |
| int argumentCount = arguments.positional.length + arguments.named.length; | |
| List<String> namedArguments = arguments.named.map((e) => e.name).toList(); | |
| return CallStructure(argumentCount, namedArguments, arguments.types.length); | |
| } | |
| ParameterStructure getParameterStructure( | |
| ir.FunctionNode node, { | |
| // TODO(johnniwinther): Remove this when type arguments are passed to | |
| // constructors like calling a generic method. | |
| bool includeTypeParameters = true, | |
| }) { | |
| // TODO(johnniwinther): Cache the computed function type. | |
| int requiredPositionalParameters = node.requiredParameterCount; | |
| int positionalParameters = node.positionalParameters.length; | |
| int typeParameters = node.typeParameters.length; | |
| List<String> namedParameters = <String>[]; | |
| Set<String> requiredNamedParameters = <String>{}; | |
| List<ir.NamedParameter> sortedNamedParameters = | |
| node.namedParameters.toList() | |
| ..sort((a, b) => a.parameterName.compareTo(b.parameterName)); | |
| for (var variable in sortedNamedParameters) { | |
| namedParameters.add(variable.parameterName); | |
| if (variable.isRequired) { | |
| requiredNamedParameters.add(variable.parameterName); | |
| } | |
| } | |
| return ParameterStructure( | |
| requiredPositionalParameters, | |
| positionalParameters, | |
| namedParameters, | |
| requiredNamedParameters, | |
| includeTypeParameters ? typeParameters : 0, | |
| ); | |
| } | |
| /// Returns the [Selector] corresponding to the invocation of [name] with | |
| /// [arguments]. | |
| Selector getInvocationSelector( | |
| ir.Name irName, | |
| int positionalArguments, | |
| List<String> namedArguments, | |
| int typeArguments, | |
| ) { | |
| Name name = getName(irName); | |
| SelectorKind kind; | |
| if (Selector.isOperatorName(name.text)) { | |
| if (name == Names.indexName || name == Names.indexSetName) { | |
| kind = SelectorKind.index_; | |
| } else { | |
| kind = SelectorKind.operator; | |
| } | |
| } else { | |
| kind = SelectorKind.call; | |
| } | |
| CallStructure callStructure = CallStructure( | |
| positionalArguments + namedArguments.length, | |
| namedArguments, | |
| typeArguments, | |
| ); | |
| return Selector(kind, name, callStructure); | |
| } | |
| Selector getGetterSelector(ir.Name irName) { | |
| Name name = Name( | |
| irName.text, | |
| irName.isPrivate ? irName.library!.importUri : null, | |
| ); | |
| return Selector.getter(name); | |
| } | |
| Selector getSetterSelector(ir.Name irName) { | |
| Name name = Name( | |
| irName.text, | |
| irName.isPrivate ? irName.library!.importUri : null, | |
| ); | |
| return Selector.setter(name); | |
| } | |
| /// Looks up [typeName] for use in the spec-string of a `JS` call. | |
| // TODO(johnniwinther): Use this in [NativeBehavior] instead of calling | |
| // the `ForeignResolver`. | |
| TypeLookup typeLookup({required bool resolveAsRaw}) { | |
| return resolveAsRaw ? _cachedTypeLookupRaw : _cachedTypeLookupFull; | |
| } | |
| late final TypeLookup _cachedTypeLookupRaw = _typeLookup(resolveAsRaw: true); | |
| late final TypeLookup _cachedTypeLookupFull = _typeLookup( | |
| resolveAsRaw: false, | |
| ); | |
| TypeLookup _typeLookup({required bool resolveAsRaw}) { | |
| bool? cachedMayLookupInMain; | |
| DartType lookup(String typeName, {bool? required}) { | |
| DartType? findInLibrary(LibraryEntity? library) { | |
| if (library != null) { | |
| ClassEntity? cls = elementEnvironment.lookupClass(library, typeName); | |
| if (cls != null) { | |
| // TODO(johnniwinther): Align semantics. | |
| return resolveAsRaw | |
| ? elementEnvironment.getRawType(cls) | |
| : elementEnvironment.getThisType(cls); | |
| } | |
| } | |
| return null; | |
| } | |
| DartType? findIn(Uri uri) { | |
| return findInLibrary(elementEnvironment.lookupLibrary(uri)); | |
| } | |
| // TODO(johnniwinther): Narrow the set of lookups based on the depending | |
| // library. | |
| // TODO(johnniwinther): Cache more results to avoid redundant lookups? | |
| cachedMayLookupInMain ??= | |
| // Tests permit lookup outside of dart: libraries. | |
| allowedNativeTest(elementEnvironment.mainLibrary!.canonicalUri); | |
| DartType? type; | |
| if (cachedMayLookupInMain!) { | |
| type ??= findInLibrary(elementEnvironment.mainLibrary); | |
| } | |
| type ??= findIn(Uris.dartCore); | |
| type ??= findIn(Uris.dartJSHelper); | |
| type ??= findIn(Uris.dartLateHelper); | |
| type ??= findIn(Uris.dartInterceptors); | |
| type ??= findIn(Uris.dartNativeTypedData); | |
| type ??= findIn(Uris.dartCollection); | |
| type ??= findIn(Uris.dartMath); | |
| type ??= findIn(Uris.dartHtml); | |
| type ??= findIn(Uris.dartHtmlCommon); | |
| type ??= findIn(Uris.dartSvg); | |
| type ??= findIn(Uris.dartWebAudio); | |
| type ??= findIn(Uris.dartWebGL); | |
| type ??= findIn(Uris.dartIndexedDB); | |
| type ??= findIn(Uris.dartTypedData); | |
| type ??= findIn(Uris.dartRti); | |
| type ??= findIn(Uris.dartMirrors); | |
| if (type == null && required!) { | |
| reporter.reportErrorMessage( | |
| currentElementSpannable, | |
| MessageKind.generic, | |
| {'text': "Type '$typeName' not found."}, | |
| ); | |
| } | |
| return type!; | |
| } | |
| return lookup; | |
| } | |
| String? _getStringArgument(ir.StaticInvocation node, int index) { | |
| return node.arguments.positional[index].accept(Stringifier()); | |
| } | |
| /// Computes the [NativeBehavior] for a call to the [JS] function. | |
| /// TODO(johnniwinther): Cache this for later use. | |
| NativeBehavior getNativeBehaviorForJsCall(ir.StaticInvocation node) { | |
| if (node.arguments.positional.length < 2 || | |
| node.arguments.named.isNotEmpty) { | |
| reporter.reportErrorMessage( | |
| currentElementSpannable, | |
| MessageKind.wrongArgumentForJS, | |
| ); | |
| return NativeBehavior(); | |
| } | |
| String? specString = _getStringArgument(node, 0); | |
| if (specString == null) { | |
| reporter.reportErrorMessage( | |
| currentElementSpannable, | |
| MessageKind.wrongArgumentForJSFirst, | |
| ); | |
| return NativeBehavior(); | |
| } | |
| String? codeString = _getStringArgument(node, 1); | |
| if (codeString == null) { | |
| reporter.reportErrorMessage( | |
| currentElementSpannable, | |
| MessageKind.wrongArgumentForJSSecond, | |
| ); | |
| return NativeBehavior(); | |
| } | |
| return NativeBehavior.ofJsCall( | |
| specString, | |
| codeString, | |
| typeLookup(resolveAsRaw: true), | |
| currentElementSpannable, | |
| reporter, | |
| commonElements, | |
| ); | |
| } | |
| /// TODO(johnniwinther): Cache this for later use. | |
| /// Computes the [NativeBehavior] for a call to the [JS_BUILTIN] | |
| /// function. | |
| NativeBehavior getNativeBehaviorForJsBuiltinCall(ir.StaticInvocation node) { | |
| if (node.arguments.positional.isEmpty) { | |
| reporter.internalError( | |
| currentElementSpannable, | |
| "JS builtin expression has no type.", | |
| ); | |
| } | |
| if (node.arguments.positional.length < 2) { | |
| reporter.internalError( | |
| currentElementSpannable, | |
| "JS builtin is missing name.", | |
| ); | |
| } | |
| String? specString = _getStringArgument(node, 0); | |
| if (specString == null) { | |
| reporter.internalError( | |
| currentElementSpannable, | |
| "Unexpected first argument.", | |
| ); | |
| } | |
| return NativeBehavior.ofJsBuiltinCall( | |
| specString, | |
| typeLookup(resolveAsRaw: true), | |
| currentElementSpannable, | |
| reporter, | |
| commonElements, | |
| ); | |
| } | |
| /// Computes the [NativeBehavior] for a call to the | |
| /// [JS_EMBEDDED_GLOBAL] function. | |
| /// TODO(johnniwinther): Cache this for later use. | |
| NativeBehavior getNativeBehaviorForJsEmbeddedGlobalCall( | |
| ir.StaticInvocation node, | |
| ) { | |
| if (node.arguments.positional.isEmpty) { | |
| reporter.internalError( | |
| currentElementSpannable, | |
| "JS embedded global expression has no type.", | |
| ); | |
| } | |
| if (node.arguments.positional.length < 2) { | |
| reporter.internalError( | |
| currentElementSpannable, | |
| "JS embedded global is missing name.", | |
| ); | |
| } | |
| if (node.arguments.positional.length > 2 || | |
| node.arguments.named.isNotEmpty) { | |
| reporter.internalError( | |
| currentElementSpannable, | |
| "JS embedded global has more than 2 arguments.", | |
| ); | |
| } | |
| String? specString = _getStringArgument(node, 0); | |
| if (specString == null) { | |
| reporter.internalError( | |
| currentElementSpannable, | |
| "Unexpected first argument.", | |
| ); | |
| } | |
| return NativeBehavior.ofJsEmbeddedGlobalCall( | |
| specString, | |
| typeLookup(resolveAsRaw: true), | |
| currentElementSpannable, | |
| reporter, | |
| commonElements, | |
| ); | |
| } | |
| /// Returns the [js.Name] for the `JsGetName` [constant] value. | |
| js.Name? getNameForJsGetName(ConstantValue constant, ModularNamer namer) { | |
| int? index = extractEnumIndexFromConstantValue( | |
| constant, | |
| commonElements.jsGetNameEnum, | |
| ); | |
| if (index == null) return null; | |
| return namer.getNameForJsGetName( | |
| currentElementSpannable, | |
| JsGetName.values[index], | |
| ); | |
| } | |
| int? extractEnumIndexFromConstantValue( | |
| ConstantValue constant, | |
| ClassEntity classElement, | |
| ) { | |
| if (constant is ConstructedConstantValue) { | |
| if (constant.type.element == classElement) { | |
| assert(constant.fields.length == 1 || constant.fields.length == 2); | |
| ConstantValue indexConstant = constant.fields.values.first; | |
| if (indexConstant is IntConstantValue) { | |
| return indexConstant.intValue.toInt(); | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| /// Computes the [ConstantValue] for the constant [node]. | |
| ConstantValue? getConstantValue( | |
| ir.Expression? node, { | |
| bool requireConstant = true, | |
| bool implicitNull = false, | |
| bool checkCasts = true, | |
| }) { | |
| if (node == null) { | |
| if (!implicitNull) { | |
| throw failedAt(currentElementSpannable, 'No expression for constant.'); | |
| } | |
| return NullConstantValue(); | |
| } | |
| final constant = node is ir.ConstantExpression ? node.constant : null; | |
| if (constant == null) { | |
| if (requireConstant) { | |
| throw UnsupportedError( | |
| 'No constant for ${DebugPrinter.prettyPrint(node)}', | |
| ); | |
| } | |
| } else { | |
| ConstantValue value = _constantValuefier.visitConstant(constant); | |
| if (!value.isConstant && !requireConstant) { | |
| return null; | |
| } | |
| return value; | |
| } | |
| return null; | |
| } | |
| /// Converts [annotations] into a list of [ConstantValue]s. | |
| List<ConstantValue> getMetadata(List<ir.Expression> annotations) { | |
| if (annotations.isEmpty) return const <ConstantValue>[]; | |
| List<ConstantValue> metadata = <ConstantValue>[]; | |
| for (var node in annotations) { | |
| // We skip the implicit cast checks for metadata to avoid circular | |
| // dependencies in the js-interop class registration. | |
| metadata.add(getConstantValue(node, checkCasts: false)!); | |
| } | |
| return metadata; | |
| } | |
| /// Returns the `noSuchMethod` [FunctionEntity] call from a | |
| /// `super.noSuchMethod` invocation within [cls]. | |
| FunctionEntity getSuperNoSuchMethod(ClassEntity cls) { | |
| while (true) { | |
| ClassEntity? superclass = elementEnvironment.getSuperClass(cls); | |
| if (superclass == null) break; | |
| MemberEntity? member = elementEnvironment.lookupLocalClassMember( | |
| superclass, | |
| Names.noSuchMethod_, | |
| ); | |
| if (member != null && !member.isAbstract) { | |
| if (member is JMethod) { | |
| if (member.parameterStructure.positionalParameters >= 1) { | |
| return member; | |
| } | |
| } | |
| // If [member] is not a valid `noSuchMethod` the target is | |
| // `Object.superNoSuchMethod`. | |
| break; | |
| } | |
| cls = superclass; | |
| } | |
| return elementEnvironment.lookupLocalClassMember( | |
| commonElements.objectClass, | |
| Names.noSuchMethod_, | |
| )! | |
| as FunctionEntity; | |
| } | |
| Iterable<LibraryEntity> get libraryListInternal { | |
| if (env.length != libraryMap.length) { | |
| // Create a [JLibrary] for each library. | |
| env.forEachLibrary((KLibraryEnv env) { | |
| getLibraryInternal(env.library, env); | |
| }); | |
| } | |
| return libraryMap.values; | |
| } | |
| JLibrary getLibraryInternal(ir.Library node, [KLibraryEnv? libraryEnv]) { | |
| return libraryMap[node] ??= _getLibraryCreate(node, libraryEnv); | |
| } | |
| JLibrary _getLibraryCreate(ir.Library node, KLibraryEnv? libraryEnv) { | |
| assert( | |
| !envIsClosed, | |
| "Environment of $this is closed. Trying to create " | |
| "library for $node.", | |
| ); | |
| Uri canonicalUri = node.importUri; | |
| String? name = node.name; | |
| if (name == null) { | |
| // Use the file name as script name. | |
| String path = canonicalUri.path; | |
| name = path.substring(path.lastIndexOf('/') + 1); | |
| } | |
| JLibrary library = createLibrary(name, canonicalUri); | |
| return libraries.register<JLibrary, KLibraryData, KLibraryEnv>( | |
| library, | |
| KLibraryData(node), | |
| libraryEnv ?? env.lookupLibrary(canonicalUri)!, | |
| ); | |
| } | |
| JClass getClassInternal(ir.Class node, [KClassEnv? classEnv]) { | |
| return classMap[node] ??= _getClassCreate(node, classEnv); | |
| } | |
| JClass _getClassCreate(ir.Class node, KClassEnv? classEnv) { | |
| assert( | |
| !envIsClosed, | |
| "Environment of $this is closed. Trying to create " | |
| "class for $node.", | |
| ); | |
| JLibrary library = getLibraryInternal(node.enclosingLibrary); | |
| classEnv ??= libraries.getEnv(library).lookupClass(node.name)!; | |
| JClass cls = createClass(library, node.name, isAbstract: node.isAbstract); | |
| return classes.register(cls, KClassData(node), classEnv); | |
| } | |
| TypeVariableEntity getTypeVariableInternal(ir.TypeParameter node) { | |
| return typeVariableMap[node] ??= _getTypeVariableCreate(node)!; | |
| } | |
| TypeVariableEntity? _getTypeVariableCreate(ir.TypeParameter node) { | |
| assert( | |
| !envIsClosed, | |
| "Environment of $this is closed. Trying to create " | |
| "type variable for $node.", | |
| ); | |
| final declaration = node.declaration; | |
| // TODO(fishythefish): Use exhaustive pattern switch. | |
| if (declaration is ir.Class) { | |
| int index = declaration.typeParameters.indexOf(node); | |
| return typeVariables.register( | |
| createTypeVariable(getClassInternal(declaration), node.name!, index), | |
| KTypeVariableData(node), | |
| ); | |
| } else if (declaration is ir.Procedure) { | |
| int index = declaration.typeParameters.indexOf(node); | |
| if (declaration.kind == ir.ProcedureKind.Factory) { | |
| ir.Class cls = declaration.enclosingClass!; | |
| return getTypeVariableInternal(cls.typeParameters[index]); | |
| } else { | |
| return typeVariables.register( | |
| createTypeVariable(getMethodInternal(declaration), node.name!, index), | |
| KTypeVariableData(node), | |
| ); | |
| } | |
| } else if (declaration is ir.LocalFunction) { | |
| // Ensure that local function type variables have been created. | |
| getLocalFunction(declaration); | |
| return typeVariableMap[node]; | |
| } | |
| throw UnsupportedError('Unsupported type parameter type node $node.'); | |
| } | |
| JConstructor getConstructorInternal(ir.Member node) { | |
| return constructorMap[node] ??= _getConstructorCreate(node); | |
| } | |
| JConstructor _getConstructorCreate(ir.Member node) { | |
| assert( | |
| !envIsClosed, | |
| "Environment of $this is closed. Trying to create " | |
| "constructor for $node.", | |
| ); | |
| ir.FunctionNode functionNode; | |
| final enclosingClass = getClassForMemberInternal(node.enclosingClass!); | |
| Name name = getName(node.name); | |
| bool isExternal = node.isExternal; | |
| JConstructor constructor; | |
| if (node is ir.Constructor) { | |
| functionNode = node.function; | |
| constructor = createGenerativeConstructor( | |
| enclosingClass, | |
| name, | |
| getParameterStructure(functionNode, includeTypeParameters: false), | |
| isExternal: isExternal, | |
| isConst: node.isConst, | |
| ); | |
| } else if (node is ir.Procedure) { | |
| functionNode = node.function; | |
| // TODO(sigmund): Check more strictly than just the class name. | |
| bool isEnvironmentConstructor = | |
| isExternal && | |
| (name.text == 'fromEnvironment' && | |
| const [ | |
| 'int', | |
| 'bool', | |
| 'String', | |
| ].contains(enclosingClass.name) || | |
| name.text == 'hasEnvironment' && enclosingClass.name == 'bool'); | |
| constructor = createFactoryConstructor( | |
| enclosingClass, | |
| name, | |
| getParameterStructure(functionNode, includeTypeParameters: false), | |
| isExternal: isExternal, | |
| isConst: node.isConst, | |
| isFromEnvironmentConstructor: isEnvironmentConstructor, | |
| ); | |
| } else { | |
| // TODO(johnniwinther): Convert `node.location` to a [SourceSpan]. | |
| throw failedAt( | |
| noLocationSpannable, | |
| "Unexpected constructor node: $node.", | |
| ); | |
| } | |
| return members.register<JConstructor, KConstructorData>( | |
| constructor, | |
| KConstructorData(node, functionNode), | |
| ); | |
| } | |
| JFunction getMethodInternal(ir.Procedure node) { | |
| // [_getMethodCreate] inserts the created function in [methodMap] so we | |
| // don't need to use ??= here. | |
| return methodMap[node] ?? _getMethodCreate(node); | |
| } | |
| JFunction _getMethodCreate(ir.Procedure node) { | |
| assert( | |
| !envIsClosed, | |
| "Environment of $this is closed. Trying to create " | |
| "function for $node.", | |
| ); | |
| late JFunction function; | |
| JLibrary library; | |
| JClass? enclosingClass; | |
| if (node.enclosingClass != null) { | |
| enclosingClass = getClassForMemberInternal(node.enclosingClass!); | |
| library = enclosingClass.library; | |
| } else { | |
| library = getLibraryInternal(node.enclosingLibrary); | |
| } | |
| Name name = getName(node.name); | |
| bool isStatic = node.isStatic; | |
| bool isExternal = node.isExternal; | |
| bool isAbstract = node.isAbstract; | |
| AsyncMarker asyncMarker = getAsyncMarker(node.function); | |
| switch (node.kind) { | |
| case ir.ProcedureKind.Factory: | |
| throw UnsupportedError("Cannot create method from factory."); | |
| case ir.ProcedureKind.Getter: | |
| function = createGetter( | |
| library, | |
| enclosingClass, | |
| name, | |
| asyncMarker, | |
| isStatic: isStatic, | |
| isExternal: isExternal, | |
| isAbstract: isAbstract, | |
| ); | |
| break; | |
| case ir.ProcedureKind.Method: | |
| case ir.ProcedureKind.Operator: | |
| function = createMethod( | |
| library, | |
| enclosingClass, | |
| name, | |
| getParameterStructure(node.function), | |
| asyncMarker, | |
| isStatic: isStatic, | |
| isExternal: isExternal, | |
| isAbstract: isAbstract, | |
| ); | |
| break; | |
| case ir.ProcedureKind.Setter: | |
| assert(asyncMarker == AsyncMarker.sync); | |
| function = createSetter( | |
| library, | |
| enclosingClass, | |
| name.setter, | |
| isStatic: isStatic, | |
| isExternal: isExternal, | |
| isAbstract: isAbstract, | |
| ); | |
| break; | |
| } | |
| members.register<JFunction, KFunctionData>( | |
| function, | |
| KFunctionData(node, node.function), | |
| ); | |
| // We need to register the function before creating the type variables. | |
| methodMap[node] = function; | |
| for (ir.TypeParameter typeParameter in node.function.typeParameters) { | |
| getTypeVariable(typeParameter); | |
| } | |
| return function; | |
| } | |
| JField getFieldInternal(ir.Field node) { | |
| return fieldMap[node] ??= _getFieldCreate(node); | |
| } | |
| JField _getFieldCreate(ir.Field node) { | |
| assert( | |
| !envIsClosed, | |
| "Environment of $this is closed. Trying to create " | |
| "field for $node.", | |
| ); | |
| JLibrary library; | |
| JClass? enclosingClass; | |
| if (node.enclosingClass != null) { | |
| enclosingClass = getClassForMemberInternal(node.enclosingClass!); | |
| library = enclosingClass.library; | |
| } else { | |
| library = getLibraryInternal(node.enclosingLibrary); | |
| } | |
| Name name = getName(node.name); | |
| bool isStatic = node.isStatic; | |
| bool isLateBackingField = false; | |
| bool isLateFinalBackingField = false; | |
| if (enclosingClass != null && !isStatic) { | |
| isLateBackingField = late_lowering.isBackingFieldForLateInstanceField( | |
| node, | |
| ); | |
| isLateFinalBackingField = late_lowering | |
| .isBackingFieldForLateFinalInstanceField(node); | |
| } | |
| JField field = createField( | |
| library, | |
| enclosingClass, | |
| name, | |
| isStatic: isStatic, | |
| isAssignable: node.hasSetter, | |
| isConst: node.isConst, | |
| ); | |
| return members.register<JField, KFieldData>( | |
| field, | |
| KFieldData( | |
| node, | |
| isLateBackingField: isLateBackingField, | |
| isLateFinalBackingField: isLateFinalBackingField, | |
| ), | |
| ); | |
| } | |
| /// NativeBasicData is need for computation of the default super class. | |
| NativeBasicData get nativeBasicData { | |
| var data = _nativeBasicData; | |
| if (data == null) { | |
| data = _nativeBasicData = nativeBasicDataBuilder.close( | |
| elementEnvironment, | |
| ); | |
| assert( | |
| _nativeBasicData != null, | |
| failedAt( | |
| noLocationSpannable, | |
| "NativeBasicData has not been computed yet.", | |
| ), | |
| ); | |
| } | |
| return data; | |
| } | |
| /// Adds libraries in [component] to the set of libraries. | |
| /// | |
| /// The main method of the first component is used as the main method for the | |
| /// compilation. | |
| void addComponent(ir.Component component) { | |
| env.addComponent(component); | |
| } | |
| BehaviorBuilder get nativeBehaviorBuilder => | |
| _nativeBehaviorBuilder ??= BehaviorBuilder( | |
| elementEnvironment, | |
| commonElements, | |
| nativeBasicData, | |
| reporter, | |
| options, | |
| ); | |
| WorldImpact computeWorldImpact( | |
| JMember member, | |
| BackendImpacts impacts, | |
| NativeResolutionEnqueuer nativeResolutionEnqueuer, | |
| BackendUsageBuilder backendUsageBuilder, | |
| CustomElementsResolutionAnalysis customElementsResolutionAnalysis, | |
| RuntimeTypesNeedBuilder rtiNeedBuilder, | |
| AnnotationsData annotationsData, | |
| ImpactBuilderData impactBuilderData, | |
| ) { | |
| KMemberData memberData = members.getData(member); | |
| ir.Member node = memberData.node; | |
| ImpactData impactData = impactBuilderData.impactData; | |
| if (retainDataForTesting) { | |
| impactDataForTesting ??= {}; | |
| impactDataForTesting![node] = impactData; | |
| } | |
| KernelImpactConverter converter = KernelImpactConverter( | |
| this, | |
| member, | |
| reporter, | |
| _constantValuefier, | |
| // TODO(johnniwinther): Pull the static type context from the cached | |
| // static types. | |
| ir.StaticTypeContext(node, typeEnvironment), | |
| impacts, | |
| nativeResolutionEnqueuer, | |
| backendUsageBuilder, | |
| customElementsResolutionAnalysis, | |
| rtiNeedBuilder, | |
| annotationsData, | |
| ); | |
| return converter.convert(impactData); | |
| } | |
| /// Returns the kernel [ir.Procedure] node for the [method]. | |
| ir.Procedure lookupProcedure(JFunction method) { | |
| return members.getData(method).node as ir.Procedure; | |
| } | |
| /// Returns the [ir.Library] corresponding to [library]. | |
| ir.Library getLibraryNode(LibraryEntity library) { | |
| return libraries.getData(library as JLibrary).library; | |
| } | |
| /// Returns the [Local] corresponding to the local function [node]. | |
| JLocalFunction getLocalFunction(ir.LocalFunction? node) { | |
| JLocalFunction? localFunction = localFunctionMap[node!] as JLocalFunction?; | |
| if (localFunction == null) { | |
| late MemberEntity memberContext; | |
| late Entity executableContext; | |
| ir.TreeNode? parent = node.parent; | |
| while (parent != null) { | |
| if (parent is ir.Member) { | |
| executableContext = memberContext = getMember(parent); | |
| break; | |
| } | |
| if (parent is ir.LocalFunction) { | |
| JLocalFunction localFunction = getLocalFunction(parent); | |
| executableContext = localFunction; | |
| memberContext = localFunction.memberContext; | |
| break; | |
| } | |
| parent = parent.parent; | |
| } | |
| String? name; | |
| late ir.FunctionNode function; | |
| if (node is ir.FunctionDeclaration) { | |
| name = node.variable.name; | |
| function = node.function; | |
| } else if (node is ir.FunctionExpression) { | |
| function = node.function; | |
| } | |
| localFunction = localFunctionMap[node] = JLocalFunction( | |
| name, | |
| memberContext, | |
| executableContext, | |
| node, | |
| ); | |
| int index = 0; | |
| List<JLocalTypeVariable> typeVariables = <JLocalTypeVariable>[]; | |
| for (ir.TypeParameter typeParameter in function.typeParameters) { | |
| typeVariables.add( | |
| typeVariableMap[typeParameter] = JLocalTypeVariable( | |
| localFunction, | |
| typeParameter.name!, | |
| index, | |
| ), | |
| ); | |
| index++; | |
| } | |
| index = 0; | |
| for (ir.TypeParameter typeParameter in function.typeParameters) { | |
| typeVariables[index].bound = getDartType(typeParameter.bound); | |
| typeVariables[index].defaultType = getDartType( | |
| typeParameter.defaultType, | |
| ); | |
| index++; | |
| } | |
| localFunction.functionType = getFunctionType(function); | |
| } | |
| return localFunction; | |
| } | |
| /// Returns `true` if [cls] implements `Function` either explicitly or through | |
| /// a `call` method. | |
| bool implementsFunction(JClass cls) { | |
| KClassData data = classes.getData(cls); | |
| OrderedTypeSet orderedTypeSet = data.orderedTypeSet!; | |
| InterfaceType? supertype = orderedTypeSet.asInstanceOf( | |
| commonElements.functionClass, | |
| getHierarchyDepth(commonElements.functionClass as JClass), | |
| ); | |
| if (supertype != null) { | |
| return true; | |
| } | |
| return data.callType?.withoutNullability is FunctionType; | |
| } | |
| /// Compute the kind of foreign helper function called by [node], if any. | |
| ForeignKind getForeignKind(ir.StaticInvocation node) { | |
| if (commonElements.isForeignHelper(getMember(node.target))) { | |
| return getForeignKindFromName(node.target.name.text); | |
| } | |
| return ForeignKind.none; | |
| } | |
| /// Computes the [InterfaceType] referenced by a call to the | |
| /// [JS_INTERCEPTOR_CONSTANT] function, if any. | |
| InterfaceType? getInterfaceTypeForJsInterceptorCall( | |
| ir.StaticInvocation node, | |
| ) { | |
| if (node.arguments.positional.length != 1 || | |
| node.arguments.named.isNotEmpty) { | |
| reporter.reportErrorMessage( | |
| currentElementSpannable, | |
| MessageKind.wrongArgumentForJSInterceptorConstant, | |
| ); | |
| } | |
| ir.Node argument = node.arguments.positional.first; | |
| if (argument is ir.TypeLiteral && argument.type is ir.InterfaceType) { | |
| return getInterfaceType(argument.type as ir.InterfaceType); | |
| } else if (argument is ir.ConstantExpression && | |
| argument.constant is ir.TypeLiteralConstant) { | |
| ir.TypeLiteralConstant constant = | |
| argument.constant as ir.TypeLiteralConstant; | |
| if (constant.type is ir.InterfaceType) { | |
| return getInterfaceType(constant.type as ir.InterfaceType); | |
| } | |
| } | |
| return null; | |
| } | |
| /// Computes the native behavior for reading the native [field]. | |
| /// TODO(johnniwinther): Cache this for later use. | |
| NativeBehavior getNativeBehaviorForFieldLoad( | |
| ir.Field field, | |
| Iterable<String> createsAnnotations, | |
| Iterable<String> returnsAnnotations, { | |
| required bool isJsInterop, | |
| }) { | |
| DartType type = getDartType(field.type); | |
| return nativeBehaviorBuilder.buildFieldLoadBehavior( | |
| type, | |
| createsAnnotations, | |
| returnsAnnotations, | |
| typeLookup(resolveAsRaw: false), | |
| isJsInterop: isJsInterop, | |
| ); | |
| } | |
| /// Computes the native behavior for writing to the native [field]. | |
| /// TODO(johnniwinther): Cache this for later use. | |
| NativeBehavior getNativeBehaviorForFieldStore(ir.Field field) { | |
| DartType type = getDartType(field.type); | |
| return nativeBehaviorBuilder.buildFieldStoreBehavior(type); | |
| } | |
| /// Computes the native behavior for calling the function or constructor | |
| /// [member]. | |
| /// TODO(johnniwinther): Cache this for later use. | |
| NativeBehavior getNativeBehaviorForMethod( | |
| ir.Member member, | |
| Iterable<String> createsAnnotations, | |
| Iterable<String> returnsAnnotations, { | |
| required bool isJsInterop, | |
| }) { | |
| late DartType type; | |
| if (member is ir.Procedure) { | |
| type = getFunctionType(member.function); | |
| } else if (member is ir.Constructor) { | |
| type = getFunctionType(member.function); | |
| } else { | |
| failedAt(currentElementSpannable, "Unexpected method node $member."); | |
| } | |
| return nativeBehaviorBuilder.buildMethodBehavior( | |
| type as FunctionType, | |
| createsAnnotations, | |
| returnsAnnotations, | |
| typeLookup(resolveAsRaw: false), | |
| isJsInterop: isJsInterop, | |
| ); | |
| } | |
| JLibrary createLibrary(String name, Uri canonicalUri) { | |
| return JLibrary(name, canonicalUri); | |
| } | |
| JClass createClass( | |
| JLibrary library, | |
| String name, { | |
| required bool isAbstract, | |
| }) { | |
| return JClass(library, name, isAbstract: isAbstract); | |
| } | |
| JTypeVariable createTypeVariable( | |
| Entity typeDeclaration, | |
| String name, | |
| int index, | |
| ) { | |
| return JTypeVariable(typeDeclaration, name, index); | |
| } | |
| JConstructor createGenerativeConstructor( | |
| JClass enclosingClass, | |
| Name name, | |
| ParameterStructure parameterStructure, { | |
| required bool isExternal, | |
| required bool isConst, | |
| }) { | |
| return JGenerativeConstructor( | |
| enclosingClass, | |
| name, | |
| parameterStructure, | |
| isExternal: isExternal, | |
| isConst: isConst, | |
| ); | |
| } | |
| // TODO(dart2js-team): Rename isFromEnvironmentConstructor to | |
| // isEnvironmentConstructor: Here, and everywhere in the compiler. | |
| JConstructor createFactoryConstructor( | |
| JClass enclosingClass, | |
| Name name, | |
| ParameterStructure parameterStructure, { | |
| required bool isExternal, | |
| required bool isConst, | |
| required bool isFromEnvironmentConstructor, | |
| }) { | |
| return JFactoryConstructor( | |
| enclosingClass, | |
| name, | |
| parameterStructure, | |
| isExternal: isExternal, | |
| isConst: isConst, | |
| isFromEnvironmentConstructor: isFromEnvironmentConstructor, | |
| ); | |
| } | |
| JFunction createGetter( | |
| JLibrary library, | |
| JClass? enclosingClass, | |
| Name name, | |
| AsyncMarker asyncMarker, { | |
| required bool isStatic, | |
| required bool isExternal, | |
| required bool isAbstract, | |
| }) { | |
| return JGetter( | |
| library, | |
| enclosingClass, | |
| name, | |
| asyncMarker, | |
| isStatic: isStatic, | |
| isExternal: isExternal, | |
| isAbstract: isAbstract, | |
| ); | |
| } | |
| JFunction createMethod( | |
| JLibrary library, | |
| JClass? enclosingClass, | |
| Name name, | |
| ParameterStructure parameterStructure, | |
| AsyncMarker asyncMarker, { | |
| required bool isStatic, | |
| required bool isExternal, | |
| required bool isAbstract, | |
| }) { | |
| return JMethod( | |
| library, | |
| enclosingClass, | |
| name, | |
| parameterStructure, | |
| asyncMarker, | |
| isStatic: isStatic, | |
| isExternal: isExternal, | |
| isAbstract: isAbstract, | |
| ); | |
| } | |
| JFunction createSetter( | |
| JLibrary library, | |
| JClass? enclosingClass, | |
| Name name, { | |
| required bool isStatic, | |
| required bool isExternal, | |
| required bool isAbstract, | |
| }) { | |
| return JSetter( | |
| library, | |
| enclosingClass, | |
| name, | |
| isStatic: isStatic, | |
| isExternal: isExternal, | |
| isAbstract: isAbstract, | |
| ); | |
| } | |
| JField createField( | |
| JLibrary library, | |
| JClass? enclosingClass, | |
| Name name, { | |
| required bool isStatic, | |
| required bool isAssignable, | |
| required bool isConst, | |
| }) { | |
| return JField( | |
| library, | |
| enclosingClass, | |
| name, | |
| isStatic: isStatic, | |
| isAssignable: isAssignable, | |
| isConst: isConst, | |
| ); | |
| } | |
| final Map<ConstantValue, Uri> _symbolLibraries = {}; | |
| @override | |
| void registerSymbolLibrary(ConstantValue value, Uri libraryUri) { | |
| _symbolLibraries[value] = libraryUri; | |
| } | |
| @override | |
| Uri? getSymbolLibraryUri(ConstantValue value) { | |
| return _symbolLibraries[value]; | |
| } | |
| } | |
| class KernelElementEnvironment extends ElementEnvironment | |
| implements KElementEnvironment { | |
| @override | |
| final KernelToElementMap elementMap; | |
| KernelElementEnvironment(this.elementMap); | |
| @override | |
| DartType get dynamicType => elementMap.types.dynamicType(); | |
| @override | |
| LibraryEntity? get mainLibrary => elementMap._mainLibrary; | |
| @override | |
| FunctionEntity? get mainFunction => elementMap._mainFunction; | |
| @override | |
| Iterable<LibraryEntity> get libraries => elementMap.libraryListInternal; | |
| @override | |
| String getLibraryName(LibraryEntity library) { | |
| return elementMap._getLibraryName(library as JLibrary); | |
| } | |
| @override | |
| InterfaceType getThisType(ClassEntity cls) { | |
| return elementMap.getThisType(cls as JClass); | |
| } | |
| @override | |
| InterfaceType getJsInteropType(ClassEntity cls) { | |
| return elementMap._getJsInteropType(cls as JClass)!; | |
| } | |
| @override | |
| InterfaceType getRawType(ClassEntity cls) { | |
| return elementMap._getRawType(cls as JClass); | |
| } | |
| @override | |
| InterfaceType getClassInstantiationToBounds(ClassEntity cls) => | |
| elementMap._getClassInstantiationToBounds(cls as JClass); | |
| @override | |
| bool isGenericClass(ClassEntity cls) { | |
| return getThisType(cls).typeArguments.isNotEmpty; | |
| } | |
| @override | |
| bool isMixinApplication(ClassEntity cls) { | |
| return elementMap._isMixinApplication(cls as JClass); | |
| } | |
| @override | |
| bool isUnnamedMixinApplication(ClassEntity cls) { | |
| return elementMap._isUnnamedMixinApplication(cls as JClass); | |
| } | |
| @override | |
| DartType getTypeVariableBound(TypeVariableEntity typeVariable) { | |
| if (typeVariable is JLocalTypeVariable) return typeVariable.bound; | |
| return elementMap.getTypeVariableBound(typeVariable as JTypeVariable); | |
| } | |
| @override | |
| List<Variance> getTypeVariableVariances(ClassEntity cls) { | |
| return elementMap.getTypeVariableVariances(cls as JClass); | |
| } | |
| @override | |
| InterfaceType createInterfaceType( | |
| ClassEntity cls, | |
| List<DartType> typeArguments, | |
| ) { | |
| return elementMap.types.interfaceType(cls, typeArguments); | |
| } | |
| @override | |
| FunctionType getFunctionType(FunctionEntity function) { | |
| return elementMap._getFunctionType(function as JFunction); | |
| } | |
| @override | |
| List<TypeVariableType> getFunctionTypeVariables(FunctionEntity function) { | |
| return elementMap._getFunctionTypeVariables(function as JFunction); | |
| } | |
| @override | |
| DartType getFieldType(FieldEntity field) { | |
| return elementMap._getFieldType(field as JField); | |
| } | |
| @override | |
| FunctionType getLocalFunctionType(covariant JLocalFunction function) { | |
| return function.functionType; | |
| } | |
| @override | |
| ConstructorEntity? lookupConstructor( | |
| ClassEntity cls, | |
| String name, { | |
| bool required = false, | |
| }) { | |
| ConstructorEntity? constructor = elementMap.lookupConstructor( | |
| cls as JClass, | |
| name, | |
| ); | |
| if (constructor == null && required) { | |
| throw failedAt( | |
| currentElementSpannable, | |
| "The constructor '$name' was not found in class '${cls.name}' " | |
| "in library ${cls.library.canonicalUri}.", | |
| ); | |
| } | |
| return constructor; | |
| } | |
| @override | |
| MemberEntity? lookupLocalClassMember( | |
| ClassEntity cls, | |
| Name name, { | |
| bool required = false, | |
| }) { | |
| MemberEntity? member = elementMap.lookupClassMember(cls as JClass, name); | |
| if (member == null && required) { | |
| throw failedAt( | |
| currentElementSpannable, | |
| "The member '$name' was not found in ${cls.name}.", | |
| ); | |
| } | |
| return member; | |
| } | |
| @override | |
| ClassEntity? getSuperClass( | |
| ClassEntity cls, { | |
| bool skipUnnamedMixinApplications = false, | |
| }) { | |
| ClassEntity? superclass = elementMap.getSuperType(cls as JClass)?.element; | |
| if (skipUnnamedMixinApplications) { | |
| while (superclass != null && | |
| elementMap._isUnnamedMixinApplication(superclass as JClass)) { | |
| superclass = elementMap.getSuperType(superclass)?.element; | |
| } | |
| } | |
| return superclass; | |
| } | |
| @override | |
| void forEachSupertype( | |
| ClassEntity cls, | |
| void Function(InterfaceType supertype) f, | |
| ) { | |
| elementMap._forEachSupertype(cls as JClass, f); | |
| } | |
| @override | |
| void forEachMixin(ClassEntity cls, void Function(ClassEntity mixin) f) { | |
| elementMap._forEachMixin(cls as JClass, f); | |
| } | |
| @override | |
| void forEachLocalClassMember( | |
| ClassEntity cls, | |
| void Function(MemberEntity member) f, | |
| ) { | |
| elementMap._forEachLocalClassMember(cls as JClass, f); | |
| } | |
| @override | |
| void forEachClassMember( | |
| ClassEntity cls, | |
| void Function(ClassEntity declarer, MemberEntity member) f, | |
| ) { | |
| elementMap._forEachClassMember(cls as JClass, f); | |
| } | |
| @override | |
| void forEachConstructor( | |
| ClassEntity cls, | |
| void Function(ConstructorEntity constructor) f, | |
| ) { | |
| elementMap._forEachConstructor(cls as JClass, f); | |
| } | |
| @override | |
| void forEachLibraryMember( | |
| LibraryEntity library, | |
| void Function(MemberEntity member) f, | |
| ) { | |
| elementMap._forEachLibraryMember(library as JLibrary, f); | |
| } | |
| @override | |
| MemberEntity? lookupLibraryMember( | |
| LibraryEntity library, | |
| String name, { | |
| bool setter = false, | |
| bool required = false, | |
| }) { | |
| MemberEntity? member = elementMap.lookupLibraryMember( | |
| library as JLibrary, | |
| name, | |
| setter: setter, | |
| ); | |
| if (member == null && required) { | |
| failedAt( | |
| currentElementSpannable, | |
| "The member '$name' was not found in library '${library.name}'.", | |
| ); | |
| } | |
| return member; | |
| } | |
| @override | |
| ClassEntity? lookupClass( | |
| LibraryEntity library, | |
| String name, { | |
| bool required = false, | |
| }) { | |
| ClassEntity? cls = elementMap.lookupClass(library as JLibrary, name); | |
| if (cls == null && required) { | |
| failedAt( | |
| currentElementSpannable, | |
| "The class '$name' was not found in library '${library.name}'.", | |
| ); | |
| } | |
| return cls; | |
| } | |
| @override | |
| void forEachClass(LibraryEntity library, void Function(ClassEntity cls) f) { | |
| elementMap._forEachClass(library as JLibrary, f); | |
| } | |
| @override | |
| LibraryEntity? lookupLibrary(Uri uri, {bool required = false}) { | |
| LibraryEntity? library = elementMap.lookupLibrary(uri); | |
| if (library == null && required) { | |
| failedAt(currentElementSpannable, "The library '$uri' was not found."); | |
| } | |
| return library; | |
| } | |
| @override | |
| Iterable<ImportEntity> getImports(covariant JLibrary library) { | |
| KLibraryData libraryData = elementMap.libraries.getData(library); | |
| return libraryData.getImports(elementMap); | |
| } | |
| @override | |
| Iterable<ConstantValue> getMemberMetadata( | |
| covariant JMember member, { | |
| bool includeParameterMetadata = false, | |
| }) { | |
| // TODO(redemption): Support includeParameterMetadata. | |
| KMemberData memberData = elementMap.members.getData(member); | |
| return memberData.getMetadata(elementMap); | |
| } | |
| @override | |
| bool isEnumClass(ClassEntity cls) { | |
| KClassData classData = elementMap.classes.getData(cls as JClass); | |
| return classData.isEnumClass; | |
| } | |
| @override | |
| ClassEntity? getEffectiveMixinClass(ClassEntity cls) { | |
| if (!isMixinApplication(cls)) return null; | |
| do { | |
| cls = elementMap.getAppliedMixin(cls as JClass)!; | |
| } while (isMixinApplication(cls)); | |
| return cls; | |
| } | |
| @override | |
| bool isLateBackingField(covariant JField field) { | |
| KFieldData fieldData = elementMap.members.getData(field) as KFieldData; | |
| return fieldData.isLateBackingField; | |
| } | |
| @override | |
| bool isLateFinalBackingField(covariant JField field) { | |
| KFieldData fieldData = elementMap.members.getData(field) as KFieldData; | |
| return fieldData.isLateFinalBackingField; | |
| } | |
| } | |
| class KernelNativeMemberResolver { | |
| static final RegExp _identifier = RegExp(r'^[a-zA-Z_$][a-zA-Z0-9_$]*$'); | |
| final KernelToElementMap _elementMap; | |
| final NativeBasicData _nativeBasicData; | |
| final NativeDataBuilder? _nativeDataBuilder; | |
| KernelNativeMemberResolver( | |
| this._elementMap, | |
| this._nativeBasicData, | |
| this._nativeDataBuilder, | |
| ); | |
| /// Computes whether [node] is native or JsInterop. | |
| void resolveNativeMember(ir.Member node, IrAnnotationData annotationData) { | |
| bool isJsInterop = _isJsInteropMember(node); | |
| if (node is ir.Procedure || node is ir.Constructor) { | |
| FunctionEntity method = _elementMap.getMember(node) as FunctionEntity; | |
| bool isNative = _processMethodAnnotations(node, annotationData); | |
| if (isNative || isJsInterop) { | |
| NativeBehavior behavior = _computeNativeMethodBehavior( | |
| method as JFunction, | |
| annotationData, | |
| isJsInterop: isJsInterop, | |
| ); | |
| _nativeDataBuilder!.setNativeMethodBehavior(method, behavior); | |
| } | |
| } else if (node is ir.Field) { | |
| FieldEntity field = _elementMap.getMember(node) as FieldEntity; | |
| bool isNative = _processFieldAnnotations(node, annotationData); | |
| if (isNative || isJsInterop) { | |
| NativeBehavior fieldLoadBehavior = _computeNativeFieldLoadBehavior( | |
| field as JField, | |
| annotationData, | |
| isJsInterop: isJsInterop, | |
| ); | |
| NativeBehavior fieldStoreBehavior = _computeNativeFieldStoreBehavior( | |
| field, | |
| ); | |
| _nativeDataBuilder! | |
| ..setNativeFieldLoadBehavior(field, fieldLoadBehavior) | |
| ..setNativeFieldStoreBehavior(field, fieldStoreBehavior); | |
| } | |
| } | |
| } | |
| /// Process the potentially native [field]. Adds information from metadata | |
| /// attributes. Returns `true` of [method] is native. | |
| bool _processFieldAnnotations( | |
| ir.Field node, | |
| IrAnnotationData annotationData, | |
| ) { | |
| if (node.isInstanceMember && | |
| _nativeBasicData.isNativeClass( | |
| _elementMap.getClass(node.enclosingClass!), | |
| )) { | |
| // Exclude non-instance (static) fields - they are not really native and | |
| // are compiled as isolate globals. Access of a property of a constructor | |
| // function or a non-method property in the prototype chain, must be coded | |
| // using a JS-call. | |
| _setNativeName(node, annotationData); | |
| return true; | |
| } else { | |
| String? name = _findJsNameFromAnnotation(node, annotationData); | |
| if (name != null) { | |
| failedAt( | |
| computeSourceSpanFromTreeNode(node), | |
| '@JSName(...) annotation is not supported for static fields: ' | |
| '$node.', | |
| ); | |
| } | |
| } | |
| return false; | |
| } | |
| /// Process the potentially native [method]. Adds information from metadata | |
| /// attributes. Returns `true` of [method] is native. | |
| bool _processMethodAnnotations( | |
| ir.Member node, | |
| IrAnnotationData annotationData, | |
| ) { | |
| if (_isNativeMethod(node, annotationData)) { | |
| if (node.enclosingClass != null && !node.isInstanceMember) { | |
| if (!_nativeBasicData.isNativeClass( | |
| _elementMap.getClass(node.enclosingClass!), | |
| )) { | |
| _elementMap.reporter.reportErrorMessage( | |
| computeSourceSpanFromTreeNode(node), | |
| MessageKind.nativeNonInstanceInNonNativeClass, | |
| ); | |
| return false; | |
| } | |
| _setNativeNameForStaticMethod(node, annotationData); | |
| } else { | |
| _setNativeName(node, annotationData); | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| /// Sets the native name of [element], either from an annotation, or | |
| /// defaulting to the Dart name. | |
| void _setNativeName(ir.Member node, IrAnnotationData annotationData) { | |
| String? name = _findJsNameFromAnnotation(node, annotationData); | |
| name ??= node.name.text; | |
| _nativeDataBuilder!.setNativeMemberName(_elementMap.getMember(node), name); | |
| } | |
| /// Sets the native name of the static native method [element], using the | |
| /// following rules: | |
| /// 1. If [element] has a @JSName annotation that is an identifier, qualify | |
| /// that identifier to the @Native name of the enclosing class | |
| /// 2. If [element] has a @JSName annotation that is not an identifier, | |
| /// use the declared @JSName as the expression | |
| /// 3. If [element] does not have a @JSName annotation, qualify the name of | |
| /// the method with the @Native name of the enclosing class. | |
| void _setNativeNameForStaticMethod( | |
| ir.Member node, | |
| IrAnnotationData annotationData, | |
| ) { | |
| String? name = _findJsNameFromAnnotation(node, annotationData); | |
| name ??= node.name.text; | |
| if (_isIdentifier(name)) { | |
| ClassEntity cls = _elementMap.getClass(node.enclosingClass!); | |
| List<String> nativeNames = _nativeBasicData.getNativeTagsOfClass(cls); | |
| if (nativeNames.length != 1) { | |
| failedAt( | |
| computeSourceSpanFromTreeNode(node), | |
| 'Unable to determine a native name for the enclosing class, ' | |
| 'options: $nativeNames', | |
| ); | |
| } | |
| _nativeDataBuilder!.setNativeMemberName( | |
| _elementMap.getMember(node), | |
| '${nativeNames[0]}.$name', | |
| ); | |
| } else { | |
| _nativeDataBuilder!.setNativeMemberName( | |
| _elementMap.getMember(node), | |
| name, | |
| ); | |
| } | |
| } | |
| bool _isIdentifier(String s) => _identifier.hasMatch(s); | |
| /// Returns the JSName annotation string or `null` if no JSName annotation is | |
| /// present. | |
| String? _findJsNameFromAnnotation( | |
| ir.Member node, | |
| IrAnnotationData annotationData, | |
| ) { | |
| return annotationData.getNativeMemberName(node); | |
| } | |
| NativeBehavior _computeNativeFieldStoreBehavior(covariant JField field) { | |
| ir.Field node = _elementMap.getMemberNode(field) as ir.Field; | |
| return _elementMap.getNativeBehaviorForFieldStore(node); | |
| } | |
| NativeBehavior _computeNativeFieldLoadBehavior( | |
| JField field, | |
| IrAnnotationData annotationData, { | |
| required bool isJsInterop, | |
| }) { | |
| ir.Field node = _elementMap.getMemberNode(field) as ir.Field; | |
| Iterable<String> createsAnnotations = annotationData.getCreatesAnnotations( | |
| node, | |
| ); | |
| Iterable<String> returnsAnnotations = annotationData.getReturnsAnnotations( | |
| node, | |
| ); | |
| return _elementMap.getNativeBehaviorForFieldLoad( | |
| node, | |
| createsAnnotations, | |
| returnsAnnotations, | |
| isJsInterop: isJsInterop, | |
| ); | |
| } | |
| NativeBehavior _computeNativeMethodBehavior( | |
| JFunction function, | |
| IrAnnotationData annotationData, { | |
| required bool isJsInterop, | |
| }) { | |
| ir.Member node = _elementMap.getMemberNode(function); | |
| Iterable<String> createsAnnotations = annotationData.getCreatesAnnotations( | |
| node, | |
| ); | |
| Iterable<String> returnsAnnotations = annotationData.getReturnsAnnotations( | |
| node, | |
| ); | |
| return _elementMap.getNativeBehaviorForMethod( | |
| node, | |
| createsAnnotations, | |
| returnsAnnotations, | |
| isJsInterop: isJsInterop, | |
| ); | |
| } | |
| bool _isNativeMethod(ir.Member node, IrAnnotationData annotationData) { | |
| if (!maybeEnableNative(node.enclosingLibrary.importUri)) return false; | |
| bool hasNativeBody = annotationData.hasNativeBody(node); | |
| // TODO(rileyporter): Move this check on non-native external usage to | |
| // js_interop_checks when `native` and `external` can be disambiguated. | |
| if (!hasNativeBody && | |
| node.isExternal && | |
| !_nativeBasicData.isJsInteropMember(_elementMap.getMember(node)) && | |
| !(node is ir.Procedure && node.hasExternalEffectPragma)) { | |
| // TODO(johnniwinther): Should we change dart:html and friends to use | |
| // `external` instead of the native body syntax? | |
| _elementMap.reporter.reportErrorMessage( | |
| computeSourceSpanFromTreeNode(node), | |
| MessageKind.nonNativeExternal, | |
| ); | |
| } | |
| return hasNativeBody; | |
| } | |
| bool _isJsInteropMember(ir.Member node) { | |
| return _nativeBasicData.isJsInteropMember(_elementMap.getMember(node)); | |
| } | |
| } | |
| ForeignKind getForeignKindFromName(String name) { | |
| switch (name) { | |
| case Identifiers.js: | |
| return ForeignKind.js; | |
| case Identifiers.jsBuiltin: | |
| return ForeignKind.jsBuiltin; | |
| case Identifiers.jsEmbeddedGlobal: | |
| return ForeignKind.jsEmbeddedGlobal; | |
| case Identifiers.jsInterceptorConstant: | |
| return ForeignKind.jsInterceptorConstant; | |
| default: | |
| return ForeignKind.none; | |
| } | |
| } |