444982f364
Creates a new 'dart:_js_shared_embedded_names' library for the names accessed from the shared 'dart:_rti' library. Migrate all of the shared symbols and uses to the new location. Change-Id: Iaa72c4522888ad630782b921b0b70d7a2626d1b9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241507 Reviewed-by: Sigmund Cherem <sigmund@google.com> Reviewed-by: Mayank Patke <fishythefish@google.com> Commit-Queue: Nicholas Shahan <nshahan@google.com>
2177 lines
78 KiB
Dart
2177 lines
78 KiB
Dart
// 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.
|
|
|
|
// @dart = 2.10
|
|
|
|
import 'package:front_end/src/api_prototype/constant_evaluator.dart' as ir;
|
|
import 'package:front_end/src/api_unstable/dart2js.dart' as ir;
|
|
import 'package:js_shared/synced/embedded_names.dart' show JsGetName;
|
|
import 'package:kernel/ast.dart' as ir;
|
|
import 'package:kernel/class_hierarchy.dart' as ir;
|
|
import 'package:kernel/core_types.dart' as ir;
|
|
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/indexed.dart';
|
|
import '../elements/names.dart';
|
|
import '../elements/types.dart';
|
|
import '../environment.dart';
|
|
import '../ir/annotations.dart';
|
|
import '../ir/constants.dart';
|
|
import '../ir/element_map.dart';
|
|
import '../ir/impact.dart';
|
|
import '../ir/impact_data.dart';
|
|
import '../ir/static_type.dart';
|
|
import '../ir/static_type_cache.dart';
|
|
import '../ir/types.dart';
|
|
import '../ir/visitors.dart';
|
|
import '../ir/util.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/locals.dart';
|
|
import '../kernel/dart2js_target.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 'element_map.dart';
|
|
import 'env.dart';
|
|
import 'kelements.dart';
|
|
import 'kernel_impact.dart';
|
|
|
|
/// Implementation of [IrToElementMap] that only supports world
|
|
/// impact computation.
|
|
class KernelToElementMap implements IrToElementMap {
|
|
final CompilerOptions options;
|
|
@override
|
|
final DiagnosticReporter reporter;
|
|
final Environment _environment;
|
|
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;
|
|
Dart2jsConstantEvaluator /*?*/ _constantEvaluator;
|
|
/*late final*/ ConstantValuefier _constantValuefier;
|
|
|
|
/// Library environment. Used for fast lookup.
|
|
KProgramEnv env = KProgramEnv();
|
|
|
|
final EntityDataEnvMap<IndexedLibrary, KLibraryData, KLibraryEnv> libraries =
|
|
EntityDataEnvMap<IndexedLibrary, KLibraryData, KLibraryEnv>();
|
|
final EntityDataEnvMap<IndexedClass, KClassData, KClassEnv> classes =
|
|
EntityDataEnvMap<IndexedClass, KClassData, KClassEnv>();
|
|
final EntityDataMap<IndexedMember, KMemberData> members =
|
|
EntityDataMap<IndexedMember, KMemberData>();
|
|
final EntityDataMap<IndexedTypeVariable, KTypeVariableData> typeVariables =
|
|
EntityDataMap<IndexedTypeVariable, 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, IndexedLibrary> libraryMap = {};
|
|
final Map<ir.Class, IndexedClass> classMap = {};
|
|
|
|
/// Map from [ir.TypeParameter] nodes to the corresponding
|
|
/// [TypeVariableEntity].
|
|
///
|
|
/// Normally the type variables are [IndexedTypeVariable]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, IndexedConstructor> constructorMap = {};
|
|
final Map<ir.Procedure, IndexedFunction> methodMap = {};
|
|
final Map<ir.Field, IndexedField> fieldMap = {};
|
|
final Map<ir.TreeNode, Local> localFunctionMap = {};
|
|
|
|
BehaviorBuilder _nativeBehaviorBuilder;
|
|
|
|
Map<KMember, Map<ir.Expression, TypeMap>> typeMapsForTesting;
|
|
Map<ir.Member, ImpactData> impactDataForTesting;
|
|
|
|
KernelToElementMap(this.reporter, this._environment, this.options) {
|
|
_elementEnvironment = KernelElementEnvironment(this);
|
|
_typeConverter = DartTypeConverter(this);
|
|
_types = KernelDartTypes(this, options);
|
|
_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) : 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 IndexedLibrary &&
|
|
spannable.libraryIndex < libraries.length) {
|
|
KLibraryEnv env = libraries.getEnv(spannable);
|
|
return computeSourceSpanFromTreeNode(env.library);
|
|
} else if (spannable is IndexedClass &&
|
|
spannable.classIndex < classes.length) {
|
|
KClassData data = classes.getData(spannable);
|
|
assert(data != null, "No data for $spannable in $this");
|
|
return computeSourceSpanFromTreeNode(data.node);
|
|
} else if (spannable is IndexedMember &&
|
|
spannable.memberIndex < members.length) {
|
|
KMemberData data = members.getData(spannable);
|
|
assert(data != null, "No data for $spannable in $this");
|
|
return computeSourceSpanFromTreeNode(data.node);
|
|
} else if (spannable is KLocalFunction) {
|
|
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(IndexedLibrary library) {
|
|
assert(checkFamily(library));
|
|
KLibraryEnv libraryEnv = libraries.getEnv(library);
|
|
return libraryEnv.library.name ?? '';
|
|
}
|
|
|
|
MemberEntity lookupLibraryMember(IndexedLibrary library, String name,
|
|
{bool setter = false}) {
|
|
assert(checkFamily(library));
|
|
KLibraryEnv libraryEnv = libraries.getEnv(library);
|
|
ir.Member member = libraryEnv.lookupMember(name, setter: setter);
|
|
return member != null ? getMember(member) : null;
|
|
}
|
|
|
|
void _forEachLibraryMember(
|
|
IndexedLibrary library, void f(MemberEntity member)) {
|
|
assert(checkFamily(library));
|
|
KLibraryEnv libraryEnv = libraries.getEnv(library);
|
|
libraryEnv.forEachMember((ir.Member node) {
|
|
f(getMember(node));
|
|
});
|
|
}
|
|
|
|
ClassEntity lookupClass(IndexedLibrary library, String name) {
|
|
assert(checkFamily(library));
|
|
KLibraryEnv libraryEnv = libraries.getEnv(library);
|
|
KClassEnv classEnv = libraryEnv.lookupClass(name);
|
|
if (classEnv != null) {
|
|
return getClassInternal(classEnv.cls, classEnv);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void _forEachClass(IndexedLibrary library, void f(ClassEntity cls)) {
|
|
assert(checkFamily(library));
|
|
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.
|
|
ClassEntity getClassForMemberInternal(ir.Class node) {
|
|
ClassEntity cls = getClassInternal(node);
|
|
classes.getEnv(cls).ensureMembers(this);
|
|
return cls;
|
|
}
|
|
|
|
MemberEntity lookupClassMember(IndexedClass cls, String name,
|
|
{bool setter = false}) {
|
|
assert(checkFamily(cls));
|
|
KClassEnv classEnv = classes.getEnv(cls);
|
|
return classEnv.lookupMember(this, name, setter: setter);
|
|
}
|
|
|
|
ConstructorEntity lookupConstructor(IndexedClass cls, String name) {
|
|
assert(checkFamily(cls));
|
|
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(IndexedClass cls) {
|
|
assert(checkFamily(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)?.element;
|
|
}
|
|
|
|
void _ensureCallType(ClassEntity cls, KClassData data) {
|
|
assert(checkFamily(cls));
|
|
if (data is KClassDataImpl && !data.isCallTypeComputed) {
|
|
MemberEntity callMember =
|
|
_elementEnvironment.lookupClassMember(cls, Identifiers.call);
|
|
if (callMember is FunctionEntity &&
|
|
callMember.isFunction &&
|
|
!callMember.isAbstract) {
|
|
data.callType = _elementEnvironment.getFunctionType(callMember);
|
|
}
|
|
data.isCallTypeComputed = true;
|
|
}
|
|
}
|
|
|
|
void _ensureThisAndRawType(ClassEntity cls, KClassData data) {
|
|
assert(checkFamily(cls));
|
|
if (data is KClassDataImpl && 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) {
|
|
assert(checkFamily(cls));
|
|
if (data is KClassDataImpl && 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) {
|
|
assert(checkFamily(cls));
|
|
if (data is KClassDataImpl && 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.legacyRawType(node),
|
|
coreTypes.objectClass,
|
|
node.enclosingLibrary));
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
TypeVariableEntity getTypeVariable(ir.TypeParameter node) =>
|
|
getTypeVariableInternal(node);
|
|
|
|
void _ensureSupertypes(ClassEntity cls, KClassData data) {
|
|
assert(checkFamily(cls));
|
|
if (data is KClassDataImpl && 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);
|
|
IndexedClass superclass = supertype.element;
|
|
KClassData superdata = classes.getData(superclass);
|
|
_ensureSupertypes(superclass, superdata);
|
|
for (InterfaceType supertype in superdata.orderedTypeSet.supertypes) {
|
|
ir.Supertype canonicalSupertype = classHierarchy
|
|
.getClassAsInstanceOf(node, getClassNode(supertype.element));
|
|
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.superclassConstraints()) {
|
|
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;
|
|
}
|
|
node.implementedTypes.forEach((ir.Supertype supertype) {
|
|
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;
|
|
IndexedClass superClass = getSuperType(sourceClass)?.element;
|
|
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;
|
|
|
|
List<DartType> getDartTypes(List<ir.DartType> types) {
|
|
List<DartType> list = <DartType>[];
|
|
types.forEach((ir.DartType type) {
|
|
list.add(getDartType(type));
|
|
});
|
|
return list;
|
|
}
|
|
|
|
/// Returns the [InterfaceType] corresponding to [type].
|
|
InterfaceType getInterfaceType(ir.InterfaceType type) =>
|
|
_typeConverter.visitType(type).withoutNullability;
|
|
|
|
/// 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.VariableDeclaration variable) {
|
|
// isCovariant implies this FunctionNode is a class Procedure.
|
|
var isCovariant =
|
|
variable.isCovariantByDeclaration || variable.isCovariantByClass;
|
|
var isFromNonNullableByDefaultLibrary = isCovariant &&
|
|
(node.parent as ir.Procedure).enclosingLibrary.isNonNullableByDefault;
|
|
return types.getTearOffParameterType(getDartType(variable.type),
|
|
isCovariant, isFromNonNullableByDefaultLibrary);
|
|
}
|
|
|
|
for (ir.VariableDeclaration variable in node.positionalParameters) {
|
|
if (parameterTypes.length == node.requiredParameterCount) {
|
|
optionalParameterTypes.add(getParameterType(variable));
|
|
} else {
|
|
parameterTypes.add(getParameterType(variable));
|
|
}
|
|
}
|
|
List<String> namedParameters = <String>[];
|
|
Set<String> requiredNamedParameters = <String>{};
|
|
List<DartType> namedParameterTypes = <DartType>[];
|
|
List<ir.VariableDeclaration> sortedNamedParameters =
|
|
node.namedParameters.toList()..sort((a, b) => a.name.compareTo(b.name));
|
|
for (ir.VariableDeclaration variable in sortedNamedParameters) {
|
|
namedParameters.add(variable.name);
|
|
namedParameterTypes.add(getParameterType(variable));
|
|
if (variable.isRequired) {
|
|
requiredNamedParameters.add(variable.name);
|
|
}
|
|
}
|
|
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).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) {
|
|
IndexedClass cls = type.element;
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
_ensureCallType(cls, data);
|
|
if (data.callType != null) {
|
|
return substByContext(data.callType, type);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
InterfaceType getThisType(IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
_ensureThisAndRawType(cls, data);
|
|
return data.thisType;
|
|
}
|
|
|
|
InterfaceType _getJsInteropType(IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
_ensureJsInteropType(cls, data);
|
|
return data.jsInteropType;
|
|
}
|
|
|
|
InterfaceType _getRawType(IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
_ensureThisAndRawType(cls, data);
|
|
return data.rawType;
|
|
}
|
|
|
|
InterfaceType _getClassInstantiationToBounds(IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
_ensureClassInstantiationToBounds(cls, data);
|
|
return data.instantiationToBounds;
|
|
}
|
|
|
|
DartType _getFieldType(IndexedField field) {
|
|
assert(checkFamily(field));
|
|
KFieldData data = members.getData(field);
|
|
return data.getFieldType(this);
|
|
}
|
|
|
|
FunctionType _getFunctionType(IndexedFunction function) {
|
|
assert(checkFamily(function));
|
|
KFunctionData data = members.getData(function);
|
|
return data.getFunctionType(this);
|
|
}
|
|
|
|
List<TypeVariableType> /*!*/ _getFunctionTypeVariables(
|
|
IndexedFunction function) {
|
|
assert(checkFamily(function));
|
|
KFunctionData data = members.getData(function);
|
|
return data.getFunctionTypeVariables(this);
|
|
}
|
|
|
|
@override
|
|
DartType getTypeVariableBound(IndexedTypeVariable typeVariable) {
|
|
assert(checkFamily(typeVariable));
|
|
KTypeVariableData data = typeVariables.getData(typeVariable);
|
|
return data.getBound(this);
|
|
}
|
|
|
|
@override
|
|
List<Variance> getTypeVariableVariances(IndexedClass cls) {
|
|
assert(checkFamily(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(IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
_ensureSupertypes(cls, data);
|
|
return data.mixedInType?.element;
|
|
}
|
|
|
|
bool _isMixinApplication(IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
_ensureSupertypes(cls, data);
|
|
return data.isMixinApplication;
|
|
}
|
|
|
|
bool _isUnnamedMixinApplication(IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
KClassEnv env = classes.getEnv(cls);
|
|
return env.isUnnamedMixinApplication;
|
|
}
|
|
|
|
void _forEachSupertype(IndexedClass cls, void f(InterfaceType supertype)) {
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
_ensureSupertypes(cls, data);
|
|
data.orderedTypeSet.supertypes.forEach(f);
|
|
}
|
|
|
|
void _forEachMixin(IndexedClass cls, void f(ClassEntity mixin)) {
|
|
assert(checkFamily(cls));
|
|
while (cls != null) {
|
|
KClassData data = classes.getData(cls);
|
|
_ensureSupertypes(cls, data);
|
|
if (data.mixedInType != null) {
|
|
f(data.mixedInType.element);
|
|
}
|
|
cls = data.supertype?.element;
|
|
}
|
|
}
|
|
|
|
void _forEachConstructor(IndexedClass cls, void f(ConstructorEntity member)) {
|
|
assert(checkFamily(cls));
|
|
KClassEnv env = classes.getEnv(cls);
|
|
env.forEachConstructor(this, f);
|
|
}
|
|
|
|
void _forEachLocalClassMember(IndexedClass cls, void f(MemberEntity member)) {
|
|
assert(checkFamily(cls));
|
|
KClassEnv env = classes.getEnv(cls);
|
|
env.forEachMember(this, (MemberEntity member) {
|
|
f(member);
|
|
});
|
|
}
|
|
|
|
void forEachInjectedClassMember(
|
|
IndexedClass cls, void f(MemberEntity member)) {
|
|
assert(checkFamily(cls));
|
|
throw UnsupportedError(
|
|
'KernelToElementMapBase._forEachInjectedClassMember');
|
|
}
|
|
|
|
void _forEachClassMember(
|
|
IndexedClass cls, void f(ClassEntity cls, MemberEntity member)) {
|
|
assert(checkFamily(cls));
|
|
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, f);
|
|
}
|
|
}
|
|
|
|
@override
|
|
InterfaceType asInstanceOf(InterfaceType type, ClassEntity cls) {
|
|
assert(checkFamily(cls));
|
|
OrderedTypeSet orderedTypeSet = getOrderedTypeSet(type.element);
|
|
InterfaceType supertype =
|
|
orderedTypeSet.asInstanceOf(cls, getHierarchyDepth(cls));
|
|
if (supertype != null) {
|
|
supertype = substByContext(supertype, type);
|
|
}
|
|
return supertype;
|
|
}
|
|
|
|
@override
|
|
OrderedTypeSet /*!*/ getOrderedTypeSet(IndexedClass cls) {
|
|
assert(checkFamily(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).supertypes;
|
|
}
|
|
|
|
/// Returns the hierarchy depth of [cls].
|
|
@override
|
|
int getHierarchyDepth(IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
_ensureSupertypes(cls, data);
|
|
return data.orderedTypeSet.maxDepth;
|
|
}
|
|
|
|
@override
|
|
Iterable<InterfaceType> /*!*/ getInterfaces(IndexedClass cls) {
|
|
assert(checkFamily(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(covariant IndexedMember member) {
|
|
assert(checkFamily(member));
|
|
return members.getData(member).node;
|
|
}
|
|
|
|
/// Returns the defining node for [cls].
|
|
ir.Class getClassNode(covariant IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
return classes.getData(cls).node;
|
|
}
|
|
|
|
/// Return the [ImportEntity] corresponding to [node].
|
|
ImportEntity getImport(ir.LibraryDependency node) {
|
|
if (node == null) return null;
|
|
ir.Library library = node.parent;
|
|
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);
|
|
|
|
ir.StaticTypeContext getStaticTypeContext(MemberEntity member) {
|
|
// TODO(johnniwinther): Cache the static type context.
|
|
return ir.StaticTypeContext(getMemberNode(member), typeEnvironment);
|
|
}
|
|
|
|
Dart2jsConstantEvaluator get constantEvaluator {
|
|
return _constantEvaluator ??=
|
|
Dart2jsConstantEvaluator(env.mainComponent, typeEnvironment,
|
|
(ir.LocatedMessage message, List<ir.LocatedMessage> context) {
|
|
reportLocatedMessage(reporter, message, context);
|
|
},
|
|
environment: _environment,
|
|
evaluationMode: options.useLegacySubtyping
|
|
? ir.EvaluationMode.weak
|
|
: ir.EvaluationMode.strong);
|
|
}
|
|
|
|
/// Returns the [Name] corresponding to [name].
|
|
Name getName(ir.Name name) {
|
|
return Name(name.text, name.isPrivate ? getLibrary(name.library) : null);
|
|
}
|
|
|
|
/// 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.VariableDeclaration> sortedNamedParameters =
|
|
node.namedParameters.toList()..sort((a, b) => a.name.compareTo(b.name));
|
|
for (var variable in sortedNamedParameters) {
|
|
namedParameters.add(variable.name);
|
|
if (variable.isRequired && !options.useLegacySubtyping) {
|
|
requiredNamedParameters.add(variable.name);
|
|
}
|
|
}
|
|
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.INDEX_NAME || name == Names.INDEX_SET_NAME) {
|
|
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 ? getLibrary(irName.library) : null);
|
|
return Selector.getter(name);
|
|
}
|
|
|
|
Selector getSetterSelector(ir.Name irName) {
|
|
Name name =
|
|
Name(irName.text, irName.isPrivate ? getLibrary(irName.library) : 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({bool resolveAsRaw = true}) {
|
|
return resolveAsRaw
|
|
? (_cachedTypeLookupRaw ??= _typeLookup(resolveAsRaw: true))
|
|
: (_cachedTypeLookupFull ??= _typeLookup(resolveAsRaw: false));
|
|
}
|
|
|
|
TypeLookup _cachedTypeLookupRaw;
|
|
TypeLookup _cachedTypeLookupFull;
|
|
|
|
TypeLookup _typeLookup({bool resolveAsRaw = true}) {
|
|
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.dart_core);
|
|
type ??= findIn(Uris.dart__js_helper);
|
|
type ??= findIn(Uris.dart__late_helper);
|
|
type ??= findIn(Uris.dart__interceptors);
|
|
type ??= findIn(Uris.dart__native_typed_data);
|
|
type ??= findIn(Uris.dart_collection);
|
|
type ??= findIn(Uris.dart_math);
|
|
type ??= findIn(Uris.dart_html);
|
|
type ??= findIn(Uris.dart_html_common);
|
|
type ??= findIn(Uris.dart_svg);
|
|
type ??= findIn(Uris.dart_web_audio);
|
|
type ??= findIn(Uris.dart_web_gl);
|
|
type ??= findIn(Uris.dart_indexed_db);
|
|
type ??= findIn(Uris.dart_typed_data);
|
|
type ??= findIn(Uris.dart__rti);
|
|
type ??= findIn(Uris.dart_mirrors);
|
|
if (type == null && required) {
|
|
reporter.reportErrorMessage(CURRENT_ELEMENT_SPANNABLE,
|
|
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(
|
|
CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS);
|
|
return NativeBehavior();
|
|
}
|
|
String specString = _getStringArgument(node, 0);
|
|
if (specString == null) {
|
|
reporter.reportErrorMessage(
|
|
CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS_FIRST);
|
|
return NativeBehavior();
|
|
}
|
|
|
|
String codeString = _getStringArgument(node, 1);
|
|
if (codeString == null) {
|
|
reporter.reportErrorMessage(
|
|
CURRENT_ELEMENT_SPANNABLE, MessageKind.WRONG_ARGUMENT_FOR_JS_SECOND);
|
|
return NativeBehavior();
|
|
}
|
|
|
|
return NativeBehavior.ofJsCall(
|
|
specString,
|
|
codeString,
|
|
typeLookup(resolveAsRaw: true),
|
|
CURRENT_ELEMENT_SPANNABLE,
|
|
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.length < 1) {
|
|
reporter.internalError(
|
|
CURRENT_ELEMENT_SPANNABLE, "JS builtin expression has no type.");
|
|
return NativeBehavior();
|
|
}
|
|
if (node.arguments.positional.length < 2) {
|
|
reporter.internalError(
|
|
CURRENT_ELEMENT_SPANNABLE, "JS builtin is missing name.");
|
|
return NativeBehavior();
|
|
}
|
|
String specString = _getStringArgument(node, 0);
|
|
if (specString == null) {
|
|
reporter.internalError(
|
|
CURRENT_ELEMENT_SPANNABLE, "Unexpected first argument.");
|
|
return NativeBehavior();
|
|
}
|
|
return NativeBehavior.ofJsBuiltinCall(
|
|
specString,
|
|
typeLookup(resolveAsRaw: true),
|
|
CURRENT_ELEMENT_SPANNABLE,
|
|
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.length < 1) {
|
|
reporter.internalError(CURRENT_ELEMENT_SPANNABLE,
|
|
"JS embedded global expression has no type.");
|
|
return NativeBehavior();
|
|
}
|
|
if (node.arguments.positional.length < 2) {
|
|
reporter.internalError(
|
|
CURRENT_ELEMENT_SPANNABLE, "JS embedded global is missing name.");
|
|
return NativeBehavior();
|
|
}
|
|
if (node.arguments.positional.length > 2 ||
|
|
node.arguments.named.isNotEmpty) {
|
|
reporter.internalError(CURRENT_ELEMENT_SPANNABLE,
|
|
"JS embedded global has more than 2 arguments.");
|
|
return NativeBehavior();
|
|
}
|
|
String specString = _getStringArgument(node, 0);
|
|
if (specString == null) {
|
|
reporter.internalError(
|
|
CURRENT_ELEMENT_SPANNABLE, "Unexpected first argument.");
|
|
return NativeBehavior();
|
|
}
|
|
return NativeBehavior.ofJsEmbeddedGlobalCall(
|
|
specString,
|
|
typeLookup(resolveAsRaw: true),
|
|
CURRENT_ELEMENT_SPANNABLE,
|
|
reporter,
|
|
commonElements);
|
|
}
|
|
|
|
/// Returns the [js.Name] for the `JsGetName` [constant] value.
|
|
js.Name getNameForJsGetName(ConstantValue constant, Namer namer) {
|
|
int index = extractEnumIndexFromConstantValue(
|
|
constant, commonElements.jsGetNameEnum);
|
|
if (index == null) return null;
|
|
return namer.getNameForJsGetName(
|
|
CURRENT_ELEMENT_SPANNABLE, 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 [expression].
|
|
ConstantValue getConstantValue(
|
|
ir.StaticTypeContext staticTypeContext, ir.Expression node,
|
|
{bool requireConstant = true,
|
|
bool implicitNull = false,
|
|
bool checkCasts = true}) {
|
|
if (node == null) {
|
|
if (!implicitNull) {
|
|
throw failedAt(
|
|
CURRENT_ELEMENT_SPANNABLE, 'No expression for constant.');
|
|
}
|
|
return NullConstantValue();
|
|
}
|
|
ir.Constant constant = constantEvaluator.evaluateOrNull(
|
|
staticTypeContext, node,
|
|
requireConstant: requireConstant);
|
|
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(
|
|
ir.StaticTypeContext staticTypeContext, List<ir.Expression> annotations) {
|
|
if (annotations.isEmpty) return const <ConstantValue>[];
|
|
List<ConstantValue /*!*/ > metadata = <ConstantValue>[];
|
|
annotations.forEach((ir.Expression node) {
|
|
// We skip the implicit cast checks for metadata to avoid circular
|
|
// dependencies in the js-interop class registration.
|
|
metadata
|
|
.add(getConstantValue(staticTypeContext, node, checkCasts: false));
|
|
});
|
|
return metadata;
|
|
}
|
|
|
|
/// Returns the `noSuchMethod` [FunctionEntity] call from a
|
|
/// `super.noSuchMethod` invocation within [cls].
|
|
FunctionEntity getSuperNoSuchMethod(ClassEntity cls) {
|
|
while (cls != null) {
|
|
cls = elementEnvironment.getSuperClass(cls);
|
|
MemberEntity member = elementEnvironment.lookupLocalClassMember(
|
|
cls, Identifiers.noSuchMethod_);
|
|
if (member != null && !member.isAbstract) {
|
|
if (member.isFunction) {
|
|
FunctionEntity function = member;
|
|
if (function.parameterStructure.positionalParameters >= 1) {
|
|
return function;
|
|
}
|
|
}
|
|
// If [member] is not a valid `noSuchMethod` the target is
|
|
// `Object.superNoSuchMethod`.
|
|
break;
|
|
}
|
|
}
|
|
FunctionEntity function = elementEnvironment.lookupLocalClassMember(
|
|
commonElements.objectClass, Identifiers.noSuchMethod_);
|
|
assert(function != null,
|
|
failedAt(cls, "No super noSuchMethod found for class $cls."));
|
|
return function;
|
|
}
|
|
|
|
Iterable<LibraryEntity> get libraryListInternal {
|
|
if (env.length != libraryMap.length) {
|
|
// Create a [KLibrary] for each library.
|
|
env.forEachLibrary((KLibraryEnv env) {
|
|
getLibraryInternal(env.library, env);
|
|
});
|
|
}
|
|
return libraryMap.values;
|
|
}
|
|
|
|
LibraryEntity getLibraryInternal(ir.Library node, [KLibraryEnv libraryEnv]) {
|
|
return libraryMap[node] ??= _getLibraryCreate(node, libraryEnv);
|
|
}
|
|
|
|
LibraryEntity _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);
|
|
}
|
|
IndexedLibrary library =
|
|
createLibrary(name, canonicalUri, node.isNonNullableByDefault);
|
|
return libraries.register(library, KLibraryData(node),
|
|
libraryEnv ?? env.lookupLibrary(canonicalUri));
|
|
}
|
|
|
|
ClassEntity getClassInternal(ir.Class node, [KClassEnv classEnv]) {
|
|
return classMap[node] ??= _getClassCreate(node, classEnv);
|
|
}
|
|
|
|
ClassEntity _getClassCreate(ir.Class node, KClassEnv classEnv) {
|
|
assert(
|
|
!envIsClosed,
|
|
"Environment of $this is closed. Trying to create "
|
|
"class for $node.");
|
|
KLibrary library = getLibraryInternal(node.enclosingLibrary);
|
|
if (classEnv == null) {
|
|
classEnv = libraries.getEnv(library).lookupClass(node.name);
|
|
}
|
|
IndexedClass cls =
|
|
createClass(library, node.name, isAbstract: node.isAbstract);
|
|
return classes.register(cls, KClassDataImpl(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.");
|
|
if (node.parent is ir.Class) {
|
|
ir.Class cls = node.parent;
|
|
int index = cls.typeParameters.indexOf(node);
|
|
return typeVariables.register(
|
|
createTypeVariable(getClassInternal(cls), node.name, index),
|
|
KTypeVariableData(node));
|
|
}
|
|
if (node.parent is ir.FunctionNode) {
|
|
ir.FunctionNode func = node.parent;
|
|
int index = func.typeParameters.indexOf(node);
|
|
if (func.parent is ir.Constructor) {
|
|
ir.Constructor constructor = func.parent;
|
|
ir.Class cls = constructor.enclosingClass;
|
|
return getTypeVariableInternal(cls.typeParameters[index]);
|
|
} else if (func.parent is ir.Procedure) {
|
|
ir.Procedure procedure = func.parent;
|
|
if (procedure.kind == ir.ProcedureKind.Factory) {
|
|
ir.Class cls = procedure.enclosingClass;
|
|
return getTypeVariableInternal(cls.typeParameters[index]);
|
|
} else {
|
|
return typeVariables.register(
|
|
createTypeVariable(
|
|
getMethodInternal(procedure), node.name, index),
|
|
KTypeVariableData(node));
|
|
}
|
|
} else if (func.parent is ir.LocalFunction) {
|
|
// Ensure that local function type variables have been created.
|
|
getLocalFunction(func.parent);
|
|
return typeVariableMap[node];
|
|
} else {
|
|
throw UnsupportedError('Unsupported function type parameter parent '
|
|
'node ${func.parent}.');
|
|
}
|
|
}
|
|
throw UnsupportedError('Unsupported type parameter type node $node.');
|
|
}
|
|
|
|
ConstructorEntity getConstructorInternal(ir.Member node) {
|
|
return constructorMap[node] ??= _getConstructorCreate(node);
|
|
}
|
|
|
|
ConstructorEntity _getConstructorCreate(ir.Member node) {
|
|
assert(
|
|
!envIsClosed,
|
|
"Environment of $this is closed. Trying to create "
|
|
"constructor for $node.");
|
|
ir.FunctionNode functionNode;
|
|
ClassEntity enclosingClass = getClassForMemberInternal(node.enclosingClass);
|
|
Name name = getName(node.name);
|
|
bool isExternal = node.isExternal;
|
|
|
|
IndexedConstructor 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(
|
|
NO_LOCATION_SPANNABLE, "Unexpected constructor node: ${node}.");
|
|
}
|
|
return members.register<IndexedConstructor, KConstructorData>(
|
|
constructor, KConstructorDataImpl(node, functionNode));
|
|
}
|
|
|
|
FunctionEntity getMethodInternal(ir.Procedure node) {
|
|
// [_getMethodCreate] inserts the created function in [methodMap] so we
|
|
// don't need to use ??= here.
|
|
return methodMap[node] ?? _getMethodCreate(node);
|
|
}
|
|
|
|
FunctionEntity _getMethodCreate(ir.Procedure node) {
|
|
assert(
|
|
!envIsClosed,
|
|
"Environment of $this is closed. Trying to create "
|
|
"function for $node.");
|
|
FunctionEntity function;
|
|
LibraryEntity library;
|
|
ClassEntity 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;
|
|
// TODO(johnniwinther): Remove `&& !node.isExternal` when #31233 is fixed.
|
|
bool isAbstract = node.isAbstract && !node.isExternal;
|
|
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<IndexedFunction, KFunctionData>(
|
|
function, KFunctionDataImpl(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;
|
|
}
|
|
|
|
FieldEntity getFieldInternal(ir.Field node) {
|
|
return fieldMap[node] ??= _getFieldCreate(node);
|
|
}
|
|
|
|
FieldEntity _getFieldCreate(ir.Field node) {
|
|
assert(
|
|
!envIsClosed,
|
|
"Environment of $this is closed. Trying to create "
|
|
"field for $node.");
|
|
LibraryEntity library;
|
|
ClassEntity 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;
|
|
IndexedField field = createField(library, enclosingClass, name,
|
|
isStatic: isStatic,
|
|
isAssignable: node.hasSetter,
|
|
isConst: node.isConst);
|
|
return members.register<IndexedField, KFieldData>(
|
|
field, KFieldDataImpl(node));
|
|
}
|
|
|
|
bool checkFamily(Entity entity) {
|
|
assert(
|
|
'$entity'.startsWith(kElementPrefix),
|
|
failedAt(entity,
|
|
"Unexpected entity $entity, expected family $kElementPrefix."));
|
|
return true;
|
|
}
|
|
|
|
/// NativeBasicData is need for computation of the default super class.
|
|
NativeBasicData get nativeBasicData {
|
|
if (_nativeBasicData == null) {
|
|
_nativeBasicData = nativeBasicDataBuilder.close(elementEnvironment);
|
|
assert(
|
|
_nativeBasicData != null,
|
|
failedAt(NO_LOCATION_SPANNABLE,
|
|
"NativeBasicData has not been computed yet."));
|
|
}
|
|
return _nativeBasicData;
|
|
}
|
|
|
|
/// 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(
|
|
KMember 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;
|
|
|
|
if (impactBuilderData.typeMapsForTesting != null) {
|
|
typeMapsForTesting ??= {};
|
|
typeMapsForTesting[member] = impactBuilderData.typeMapsForTesting;
|
|
}
|
|
ImpactData impactData = impactBuilderData.impactData;
|
|
memberData.staticTypes = impactBuilderData.cachedStaticTypes;
|
|
if (retainDataForTesting) {
|
|
impactDataForTesting ??= {};
|
|
impactDataForTesting[node] = impactData;
|
|
}
|
|
KernelImpactConverter converter = KernelImpactConverter(
|
|
this,
|
|
member,
|
|
reporter,
|
|
options,
|
|
_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);
|
|
}
|
|
|
|
StaticTypeCache getCachedStaticTypes(KMember member) {
|
|
StaticTypeCache staticTypes = members.getData(member).staticTypes;
|
|
assert(staticTypes != null, "No static types cached for $member.");
|
|
return staticTypes;
|
|
}
|
|
|
|
Map<ir.Expression, TypeMap> getTypeMapsForTesting(KMember member) {
|
|
return typeMapsForTesting[member];
|
|
}
|
|
|
|
/// Returns the kernel [ir.Procedure] node for the [method].
|
|
ir.Procedure lookupProcedure(KFunction method) {
|
|
return members.getData(method).node;
|
|
}
|
|
|
|
/// Returns the [ir.Library] corresponding to [library].
|
|
ir.Library getLibraryNode(LibraryEntity library) {
|
|
return libraries.getData(library).library;
|
|
}
|
|
|
|
/// Returns the [Local] corresponding to the local function [node].
|
|
Local getLocalFunction(ir.LocalFunction node) {
|
|
KLocalFunction localFunction = localFunctionMap[node];
|
|
if (localFunction == null) {
|
|
MemberEntity memberContext;
|
|
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) {
|
|
KLocalFunction localFunction = getLocalFunction(parent);
|
|
executableContext = localFunction;
|
|
memberContext = localFunction.memberContext;
|
|
break;
|
|
}
|
|
parent = parent.parent;
|
|
}
|
|
String name;
|
|
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] =
|
|
KLocalFunction(name, memberContext, executableContext, node);
|
|
int index = 0;
|
|
List<KLocalTypeVariable> typeVariables = <KLocalTypeVariable>[];
|
|
for (ir.TypeParameter typeParameter in function.typeParameters) {
|
|
typeVariables.add(typeVariableMap[typeParameter] =
|
|
KLocalTypeVariable(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(IndexedClass cls) {
|
|
assert(checkFamily(cls));
|
|
KClassData data = classes.getData(cls);
|
|
OrderedTypeSet orderedTypeSet = data.orderedTypeSet;
|
|
InterfaceType supertype = orderedTypeSet.asInstanceOf(
|
|
commonElements.functionClass,
|
|
getHierarchyDepth(commonElements.functionClass));
|
|
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))) {
|
|
switch (node.target.name.text) {
|
|
case Identifiers.JS:
|
|
return ForeignKind.JS;
|
|
case Identifiers.JS_BUILTIN:
|
|
return ForeignKind.JS_BUILTIN;
|
|
case Identifiers.JS_EMBEDDED_GLOBAL:
|
|
return ForeignKind.JS_EMBEDDED_GLOBAL;
|
|
case Identifiers.JS_INTERCEPTOR_CONSTANT:
|
|
return ForeignKind.JS_INTERCEPTOR_CONSTANT;
|
|
}
|
|
}
|
|
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(CURRENT_ELEMENT_SPANNABLE,
|
|
MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT);
|
|
}
|
|
ir.Node argument = node.arguments.positional.first;
|
|
if (argument is ir.TypeLiteral && argument.type is ir.InterfaceType) {
|
|
return getInterfaceType(argument.type);
|
|
} else if (argument is ir.ConstantExpression &&
|
|
argument.constant is ir.TypeLiteralConstant) {
|
|
ir.TypeLiteralConstant constant = argument.constant;
|
|
if (constant.type is ir.InterfaceType) {
|
|
return getInterfaceType(constant.type);
|
|
}
|
|
}
|
|
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,
|
|
{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,
|
|
{bool isJsInterop}) {
|
|
DartType type;
|
|
if (member is ir.Procedure) {
|
|
type = getFunctionType(member.function);
|
|
} else if (member is ir.Constructor) {
|
|
type = getFunctionType(member.function);
|
|
} else {
|
|
failedAt(CURRENT_ELEMENT_SPANNABLE, "Unexpected method node $member.");
|
|
}
|
|
return nativeBehaviorBuilder.buildMethodBehavior(type, createsAnnotations,
|
|
returnsAnnotations, typeLookup(resolveAsRaw: false),
|
|
isJsInterop: isJsInterop);
|
|
}
|
|
|
|
IndexedLibrary createLibrary(
|
|
String name, Uri canonicalUri, bool isNonNullableByDefault) {
|
|
return KLibrary(name, canonicalUri, isNonNullableByDefault);
|
|
}
|
|
|
|
IndexedClass createClass(LibraryEntity library, String name,
|
|
{/*required*/ bool isAbstract}) {
|
|
return KClass(library, name, isAbstract: isAbstract);
|
|
}
|
|
|
|
TypeVariableEntity createTypeVariable(
|
|
Entity typeDeclaration, String name, int index) {
|
|
return KTypeVariable(typeDeclaration, name, index);
|
|
}
|
|
|
|
IndexedConstructor createGenerativeConstructor(ClassEntity enclosingClass,
|
|
Name name, ParameterStructure parameterStructure,
|
|
{/*required*/ bool isExternal, /*required*/ bool isConst}) {
|
|
return KGenerativeConstructor(enclosingClass, name, parameterStructure,
|
|
isExternal: isExternal, isConst: isConst);
|
|
}
|
|
|
|
// TODO(dart2js-team): Rename isFromEnvironmentConstructor to
|
|
// isEnvironmentConstructor: Here, and everywhere in the compiler.
|
|
IndexedConstructor createFactoryConstructor(ClassEntity enclosingClass,
|
|
Name name, ParameterStructure parameterStructure,
|
|
{/*required*/ bool isExternal,
|
|
/*required*/ bool isConst,
|
|
/*required*/ bool isFromEnvironmentConstructor}) {
|
|
return KFactoryConstructor(enclosingClass, name, parameterStructure,
|
|
isExternal: isExternal,
|
|
isConst: isConst,
|
|
isFromEnvironmentConstructor: isFromEnvironmentConstructor);
|
|
}
|
|
|
|
IndexedFunction createGetter(LibraryEntity library,
|
|
ClassEntity enclosingClass, Name name, AsyncMarker asyncMarker,
|
|
{/*required*/ bool isStatic,
|
|
/*required*/ bool isExternal,
|
|
/*required*/ bool isAbstract}) {
|
|
return KGetter(library, enclosingClass, name, asyncMarker,
|
|
isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract);
|
|
}
|
|
|
|
IndexedFunction createMethod(
|
|
LibraryEntity library,
|
|
ClassEntity enclosingClass,
|
|
Name name,
|
|
ParameterStructure parameterStructure,
|
|
AsyncMarker asyncMarker,
|
|
{/*required*/ bool isStatic,
|
|
/*required*/ bool isExternal,
|
|
/*required*/ bool isAbstract}) {
|
|
return KMethod(
|
|
library, enclosingClass, name, parameterStructure, asyncMarker,
|
|
isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract);
|
|
}
|
|
|
|
IndexedFunction createSetter(
|
|
LibraryEntity library, ClassEntity enclosingClass, Name name,
|
|
{/*required*/ bool isStatic,
|
|
/*required*/ bool isExternal,
|
|
/*required*/ bool isAbstract}) {
|
|
return KSetter(library, enclosingClass, name,
|
|
isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract);
|
|
}
|
|
|
|
IndexedField createField(
|
|
LibraryEntity library, ClassEntity enclosingClass, Name name,
|
|
{/*required*/ bool isStatic,
|
|
/*required*/ bool isAssignable,
|
|
/*required*/ bool isConst}) {
|
|
return KField(library, enclosingClass, name,
|
|
isStatic: isStatic, isAssignable: isAssignable, isConst: isConst);
|
|
}
|
|
}
|
|
|
|
class KernelElementEnvironment extends ElementEnvironment
|
|
implements KElementEnvironment {
|
|
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);
|
|
}
|
|
|
|
@override
|
|
InterfaceType getThisType(ClassEntity cls) {
|
|
return elementMap.getThisType(cls);
|
|
}
|
|
|
|
@override
|
|
InterfaceType getJsInteropType(ClassEntity cls) {
|
|
return elementMap._getJsInteropType(cls);
|
|
}
|
|
|
|
@override
|
|
InterfaceType getRawType(ClassEntity cls) {
|
|
return elementMap._getRawType(cls);
|
|
}
|
|
|
|
@override
|
|
InterfaceType getClassInstantiationToBounds(ClassEntity cls) =>
|
|
elementMap._getClassInstantiationToBounds(cls);
|
|
|
|
@override
|
|
bool isGenericClass(ClassEntity cls) {
|
|
return getThisType(cls).typeArguments.isNotEmpty;
|
|
}
|
|
|
|
@override
|
|
bool isMixinApplication(ClassEntity cls) {
|
|
return elementMap._isMixinApplication(cls);
|
|
}
|
|
|
|
@override
|
|
bool isUnnamedMixinApplication(ClassEntity cls) {
|
|
return elementMap._isUnnamedMixinApplication(cls);
|
|
}
|
|
|
|
@override
|
|
DartType getTypeVariableBound(TypeVariableEntity typeVariable) {
|
|
if (typeVariable is KLocalTypeVariable) return typeVariable.bound;
|
|
return elementMap.getTypeVariableBound(typeVariable);
|
|
}
|
|
|
|
@override
|
|
List<Variance> getTypeVariableVariances(ClassEntity cls) {
|
|
return elementMap.getTypeVariableVariances(cls);
|
|
}
|
|
|
|
@override
|
|
InterfaceType createInterfaceType(
|
|
ClassEntity cls, List<DartType> typeArguments) {
|
|
return elementMap.types.interfaceType(cls, typeArguments);
|
|
}
|
|
|
|
@override
|
|
FunctionType getFunctionType(FunctionEntity function) {
|
|
return elementMap._getFunctionType(function);
|
|
}
|
|
|
|
@override
|
|
List<TypeVariableType> getFunctionTypeVariables(FunctionEntity function) {
|
|
return elementMap._getFunctionTypeVariables(function);
|
|
}
|
|
|
|
@override
|
|
DartType getFieldType(FieldEntity field) {
|
|
return elementMap._getFieldType(field);
|
|
}
|
|
|
|
@override
|
|
FunctionType getLocalFunctionType(covariant KLocalFunction function) {
|
|
return function.functionType;
|
|
}
|
|
|
|
@override
|
|
ConstructorEntity lookupConstructor(ClassEntity cls, String name,
|
|
{bool required = false}) {
|
|
ConstructorEntity constructor = elementMap.lookupConstructor(cls, name);
|
|
if (constructor == null && required) {
|
|
throw failedAt(
|
|
CURRENT_ELEMENT_SPANNABLE,
|
|
"The constructor '$name' was not found in class '${cls.name}' "
|
|
"in library ${cls.library.canonicalUri}.");
|
|
}
|
|
return constructor;
|
|
}
|
|
|
|
@override
|
|
MemberEntity lookupLocalClassMember(ClassEntity cls, String name,
|
|
{bool setter = false, bool required = false}) {
|
|
MemberEntity member =
|
|
elementMap.lookupClassMember(cls, name, setter: setter);
|
|
if (member == null && required) {
|
|
throw failedAt(CURRENT_ELEMENT_SPANNABLE,
|
|
"The member '$name' was not found in ${cls.name}.");
|
|
}
|
|
return member;
|
|
}
|
|
|
|
@override
|
|
ClassEntity getSuperClass(ClassEntity cls,
|
|
{bool skipUnnamedMixinApplications = false}) {
|
|
assert(elementMap.checkFamily(cls));
|
|
ClassEntity superclass = elementMap.getSuperType(cls)?.element;
|
|
if (skipUnnamedMixinApplications) {
|
|
while (superclass != null &&
|
|
elementMap._isUnnamedMixinApplication(superclass)) {
|
|
superclass = elementMap.getSuperType(superclass)?.element;
|
|
}
|
|
}
|
|
return superclass;
|
|
}
|
|
|
|
@override
|
|
void forEachSupertype(ClassEntity cls, void f(InterfaceType supertype)) {
|
|
elementMap._forEachSupertype(cls, f);
|
|
}
|
|
|
|
@override
|
|
void forEachMixin(ClassEntity cls, void f(ClassEntity mixin)) {
|
|
elementMap._forEachMixin(cls, f);
|
|
}
|
|
|
|
@override
|
|
void forEachLocalClassMember(ClassEntity cls, void f(MemberEntity member)) {
|
|
elementMap._forEachLocalClassMember(cls, f);
|
|
}
|
|
|
|
@override
|
|
void forEachClassMember(
|
|
ClassEntity cls, void f(ClassEntity declarer, MemberEntity member)) {
|
|
elementMap._forEachClassMember(cls, f);
|
|
}
|
|
|
|
@override
|
|
void forEachConstructor(
|
|
ClassEntity cls, void f(ConstructorEntity constructor)) {
|
|
elementMap._forEachConstructor(cls, f);
|
|
}
|
|
|
|
@override
|
|
void forEachLibraryMember(
|
|
LibraryEntity library, void f(MemberEntity member)) {
|
|
elementMap._forEachLibraryMember(library, f);
|
|
}
|
|
|
|
@override
|
|
MemberEntity lookupLibraryMember(LibraryEntity library, String name,
|
|
{bool setter = false, bool required = false}) {
|
|
MemberEntity member =
|
|
elementMap.lookupLibraryMember(library, name, setter: setter);
|
|
if (member == null && required) {
|
|
failedAt(CURRENT_ELEMENT_SPANNABLE,
|
|
"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, name);
|
|
if (cls == null && required) {
|
|
failedAt(CURRENT_ELEMENT_SPANNABLE,
|
|
"The class '$name' was not found in library '${library.name}'.");
|
|
}
|
|
return cls;
|
|
}
|
|
|
|
@override
|
|
void forEachClass(LibraryEntity library, void f(ClassEntity cls)) {
|
|
elementMap._forEachClass(library, f);
|
|
}
|
|
|
|
@override
|
|
LibraryEntity lookupLibrary(Uri uri, {bool required = false}) {
|
|
LibraryEntity library = elementMap.lookupLibrary(uri);
|
|
if (library == null && required) {
|
|
failedAt(CURRENT_ELEMENT_SPANNABLE, "The library '$uri' was not found.");
|
|
}
|
|
return library;
|
|
}
|
|
|
|
@override
|
|
Iterable<ImportEntity> getImports(covariant IndexedLibrary library) {
|
|
assert(elementMap.checkFamily(library));
|
|
KLibraryData libraryData = elementMap.libraries.getData(library);
|
|
return libraryData.getImports(elementMap);
|
|
}
|
|
|
|
@override
|
|
Iterable<ConstantValue> getMemberMetadata(covariant IndexedMember member,
|
|
{bool includeParameterMetadata = false}) {
|
|
// TODO(redemption): Support includeParameterMetadata.
|
|
assert(elementMap.checkFamily(member));
|
|
KMemberData memberData = elementMap.members.getData(member);
|
|
return memberData.getMetadata(elementMap);
|
|
}
|
|
|
|
@override
|
|
bool isEnumClass(ClassEntity cls) {
|
|
assert(elementMap.checkFamily(cls));
|
|
KClassData classData = elementMap.classes.getData(cls);
|
|
return classData.isEnumClass;
|
|
}
|
|
|
|
@override
|
|
ClassEntity getEffectiveMixinClass(ClassEntity cls) {
|
|
if (!isMixinApplication(cls)) return null;
|
|
do {
|
|
cls = elementMap.getAppliedMixin(cls);
|
|
} while (isMixinApplication(cls));
|
|
return cls;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
assert(annotationData != null);
|
|
bool isJsInterop = _isJsInteropMember(node);
|
|
if (node is ir.Procedure || node is ir.Constructor) {
|
|
FunctionEntity method = _elementMap.getMember(node);
|
|
bool isNative = _processMethodAnnotations(node, annotationData);
|
|
if (isNative || isJsInterop) {
|
|
NativeBehavior behavior = _computeNativeMethodBehavior(
|
|
method, annotationData,
|
|
isJsInterop: isJsInterop);
|
|
_nativeDataBuilder.setNativeMethodBehavior(method, behavior);
|
|
}
|
|
} else if (node is ir.Field) {
|
|
FieldEntity field = _elementMap.getMember(node);
|
|
bool isNative = _processFieldAnnotations(node, annotationData);
|
|
if (isNative || isJsInterop) {
|
|
NativeBehavior fieldLoadBehavior = _computeNativeFieldLoadBehavior(
|
|
field, annotationData,
|
|
isJsInterop: isJsInterop);
|
|
NativeBehavior fieldStoreBehavior =
|
|
_computeNativeFieldStoreBehavior(field);
|
|
_nativeDataBuilder.setNativeFieldLoadBehavior(field, fieldLoadBehavior);
|
|
_nativeDataBuilder.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) {
|
|
assert(annotationData != null);
|
|
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) {
|
|
assert(annotationData != null);
|
|
if (_isNativeMethod(node, annotationData)) {
|
|
if (node.enclosingClass != null && !node.isInstanceMember) {
|
|
if (!_nativeBasicData
|
|
.isNativeClass(_elementMap.getClass(node.enclosingClass))) {
|
|
_elementMap.reporter.reportErrorMessage(
|
|
computeSourceSpanFromTreeNode(node),
|
|
MessageKind.NATIVE_NON_INSTANCE_IN_NON_NATIVE_CLASS);
|
|
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) {
|
|
assert(annotationData != null);
|
|
return annotationData.getNativeMemberName(node);
|
|
}
|
|
|
|
NativeBehavior _computeNativeFieldStoreBehavior(covariant KField field) {
|
|
ir.Field node = _elementMap.getMemberNode(field);
|
|
return _elementMap.getNativeBehaviorForFieldStore(node);
|
|
}
|
|
|
|
NativeBehavior _computeNativeFieldLoadBehavior(
|
|
KField field, IrAnnotationData annotationData,
|
|
{bool isJsInterop}) {
|
|
assert(annotationData != null);
|
|
ir.Field node = _elementMap.getMemberNode(field);
|
|
Iterable<String> createsAnnotations =
|
|
annotationData.getCreatesAnnotations(node);
|
|
Iterable<String> returnsAnnotations =
|
|
annotationData.getReturnsAnnotations(node);
|
|
return _elementMap.getNativeBehaviorForFieldLoad(
|
|
node, createsAnnotations, returnsAnnotations,
|
|
isJsInterop: isJsInterop);
|
|
}
|
|
|
|
NativeBehavior _computeNativeMethodBehavior(
|
|
KFunction function, IrAnnotationData annotationData,
|
|
{bool isJsInterop}) {
|
|
assert(annotationData != null);
|
|
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) {
|
|
assert(annotationData != null);
|
|
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))) {
|
|
// TODO(johnniwinther): Should we change dart:html and friends to use
|
|
// `external` instead of the native body syntax?
|
|
_elementMap.reporter.reportErrorMessage(
|
|
computeSourceSpanFromTreeNode(node), MessageKind.NON_NATIVE_EXTERNAL);
|
|
}
|
|
return hasNativeBody;
|
|
}
|
|
|
|
bool _isJsInteropMember(ir.Member node) {
|
|
return _nativeBasicData.isJsInteropMember(_elementMap.getMember(node));
|
|
}
|
|
}
|
|
|
|
DiagnosticMessage _createDiagnosticMessage(
|
|
DiagnosticReporter reporter, ir.LocatedMessage message) {
|
|
SourceSpan sourceSpan = SourceSpan(
|
|
message.uri, message.charOffset, message.charOffset + message.length);
|
|
return reporter.createMessage(
|
|
sourceSpan, MessageKind.GENERIC, {'text': message.problemMessage});
|
|
}
|
|
|
|
void reportLocatedMessage(DiagnosticReporter reporter,
|
|
ir.LocatedMessage message, List<ir.LocatedMessage> context) {
|
|
DiagnosticMessage diagnosticMessage =
|
|
_createDiagnosticMessage(reporter, message);
|
|
List<DiagnosticMessage> infos = [];
|
|
for (ir.LocatedMessage message in context) {
|
|
infos.add(_createDiagnosticMessage(reporter, message));
|
|
}
|
|
reporter.reportError(diagnosticMessage, infos);
|
|
}
|