Index: pkg/compiler/lib/src/js_backend/backend.dart |
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart |
deleted file mode 100644 |
index dfd5a92a974849f20cdaecc6a8e44d7428143636..0000000000000000000000000000000000000000 |
--- a/pkg/compiler/lib/src/js_backend/backend.dart |
+++ /dev/null |
@@ -1,2450 +0,0 @@ |
-// Copyright (c) 2012, 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. |
- |
-part of js_backend; |
- |
-const VERBOSE_OPTIMIZER_HINTS = false; |
- |
-class JavaScriptItemCompilationContext extends ItemCompilationContext { |
- final Set<HInstruction> boundsChecked = new Set<HInstruction>(); |
- final Set<HInstruction> allocatedFixedLists = new Set<HInstruction>(); |
-} |
- |
-/* |
- * Invariants: |
- * canInline(function) implies canInline(function, insideLoop:true) |
- * !canInline(function, insideLoop: true) implies !canInline(function) |
- */ |
-class FunctionInlineCache { |
- final Map<FunctionElement, bool> canBeInlined = |
- new Map<FunctionElement, bool>(); |
- |
- final Map<FunctionElement, bool> canBeInlinedInsideLoop = |
- new Map<FunctionElement, bool>(); |
- |
- // Returns [:true:]/[:false:] if we have a cached decision. |
- // Returns [:null:] otherwise. |
- bool canInline(FunctionElement element, {bool insideLoop}) { |
- return insideLoop ? canBeInlinedInsideLoop[element] : canBeInlined[element]; |
- } |
- |
- void markAsInlinable(FunctionElement element, {bool insideLoop}) { |
- if (insideLoop) { |
- canBeInlinedInsideLoop[element] = true; |
- } else { |
- // If we can inline a function outside a loop then we should do it inside |
- // a loop as well. |
- canBeInlined[element] = true; |
- canBeInlinedInsideLoop[element] = true; |
- } |
- } |
- |
- void markAsNonInlinable(FunctionElement element, {bool insideLoop}) { |
- if (insideLoop == null || insideLoop) { |
- // If we can't inline a function inside a loop, then we should not inline |
- // it outside a loop either. |
- canBeInlined[element] = false; |
- canBeInlinedInsideLoop[element] = false; |
- } else { |
- canBeInlined[element] = false; |
- } |
- } |
-} |
- |
-class JavaScriptBackend extends Backend { |
- static final Uri DART_JS_HELPER = new Uri(scheme: 'dart', path: '_js_helper'); |
- static final Uri DART_INTERCEPTORS = |
- new Uri(scheme: 'dart', path: '_interceptors'); |
- static final Uri DART_INTERNAL = |
- new Uri(scheme: 'dart', path: '_internal'); |
- static final Uri DART_FOREIGN_HELPER = |
- new Uri(scheme: 'dart', path: '_foreign_helper'); |
- static final Uri DART_JS_MIRRORS = |
- new Uri(scheme: 'dart', path: '_js_mirrors'); |
- static final Uri DART_JS_NAMES = |
- new Uri(scheme: 'dart', path: '_js_names'); |
- static final Uri DART_ISOLATE_HELPER = |
- new Uri(scheme: 'dart', path: '_isolate_helper'); |
- static final Uri DART_HTML = |
- new Uri(scheme: 'dart', path: 'html'); |
- |
- static const String INVOKE_ON = '_getCachedInvocation'; |
- static const String START_ROOT_ISOLATE = 'startRootIsolate'; |
- |
- |
- /// The list of functions for classes in the [internalLibrary] that we want |
- /// to inline always. Any function in this list must be inlinable with |
- /// respect to the conditions used in [InlineWeeder.canInline], except for |
- /// size/complexity heuristics. |
- static const Map<String, List<String>> ALWAYS_INLINE = |
- const <String, List<String>> { |
- 'IterableMixinWorkaround': const <String>['forEach'], |
- }; |
- |
- /// List of [FunctionElement]s that we want to inline always. This list is |
- /// filled when resolution is complete by looking up in [internalLibrary]. |
- List<FunctionElement> functionsToAlwaysInline; |
- |
- /// Reference to the internal library to lookup functions to always inline. |
- LibraryElement internalLibrary; |
- |
- |
- /// Set of classes that need to be considered for reflection although not |
- /// otherwise visible during resolution. |
- Iterable<ClassElement> get classesRequiredForReflection { |
- // TODO(herhut): Clean this up when classes needed for rti are tracked. |
- return [closureClass, jsIndexableClass]; |
- } |
- |
- SsaBuilderTask builder; |
- SsaOptimizerTask optimizer; |
- SsaCodeGeneratorTask generator; |
- CodeEmitterTask emitter; |
- |
- /** |
- * The generated code as a js AST for compiled methods. |
- */ |
- Map<Element, jsAst.Expression> get generatedCode { |
- return compiler.enqueuer.codegen.generatedCode; |
- } |
- |
- FunctionInlineCache inlineCache = new FunctionInlineCache(); |
- |
- LibraryElement jsHelperLibrary; |
- LibraryElement interceptorsLibrary; |
- LibraryElement foreignLibrary; |
- LibraryElement isolateHelperLibrary; |
- |
- ClassElement closureClass; |
- ClassElement boundClosureClass; |
- Element assertMethod; |
- Element invokeOnMethod; |
- |
- ClassElement jsInterceptorClass; |
- ClassElement jsStringClass; |
- ClassElement jsArrayClass; |
- ClassElement jsNumberClass; |
- ClassElement jsIntClass; |
- ClassElement jsDoubleClass; |
- ClassElement jsNullClass; |
- ClassElement jsBoolClass; |
- ClassElement jsPlainJavaScriptObjectClass; |
- ClassElement jsUnknownJavaScriptObjectClass; |
- |
- ClassElement jsIndexableClass; |
- ClassElement jsMutableIndexableClass; |
- |
- ClassElement jsMutableArrayClass; |
- ClassElement jsFixedArrayClass; |
- ClassElement jsExtendableArrayClass; |
- ClassElement jsPositiveIntClass; |
- ClassElement jsUInt32Class; |
- ClassElement jsUInt31Class; |
- |
- Element jsIndexableLength; |
- Element jsArrayTypedConstructor; |
- Element jsArrayRemoveLast; |
- Element jsArrayAdd; |
- Element jsStringSplit; |
- Element jsStringToString; |
- Element jsStringOperatorAdd; |
- Element objectEquals; |
- |
- ClassElement typeLiteralClass; |
- ClassElement mapLiteralClass; |
- ClassElement constMapLiteralClass; |
- ClassElement typeVariableClass; |
- ConstructorElement mapLiteralConstructor; |
- ConstructorElement mapLiteralConstructorEmpty; |
- |
- ClassElement noSideEffectsClass; |
- ClassElement noThrowsClass; |
- ClassElement noInlineClass; |
- ClassElement irRepresentationClass; |
- |
- Element getInterceptorMethod; |
- Element interceptedNames; |
- |
- ClassElement jsInvocationMirrorClass; |
- |
- /// If [true], the compiler will emit code that writes the name of the current |
- /// method together with its class and library to the console the first time |
- /// the method is called. |
- static const bool TRACE_CALLS = false; |
- Element traceHelper; |
- |
- /** |
- * This element is a top-level variable (in generated output) that the |
- * compiler initializes to a datastructure used to map from a Type to the |
- * interceptor. See declaration of `mapTypeToInterceptor` in |
- * `interceptors.dart`. |
- */ |
- Element mapTypeToInterceptor; |
- |
- TypeMask get stringType => compiler.typesTask.stringType; |
- TypeMask get doubleType => compiler.typesTask.doubleType; |
- TypeMask get intType => compiler.typesTask.intType; |
- TypeMask get uint32Type => compiler.typesTask.uint32Type; |
- TypeMask get uint31Type => compiler.typesTask.uint31Type; |
- TypeMask get positiveIntType => compiler.typesTask.positiveIntType; |
- TypeMask get numType => compiler.typesTask.numType; |
- TypeMask get boolType => compiler.typesTask.boolType; |
- TypeMask get dynamicType => compiler.typesTask.dynamicType; |
- TypeMask get nullType => compiler.typesTask.nullType; |
- TypeMask get emptyType => const TypeMask.nonNullEmpty(); |
- |
- TypeMask _indexablePrimitiveTypeCache; |
- TypeMask get indexablePrimitiveType { |
- if (_indexablePrimitiveTypeCache == null) { |
- _indexablePrimitiveTypeCache = |
- new TypeMask.nonNullSubtype(jsIndexableClass, compiler.world); |
- } |
- return _indexablePrimitiveTypeCache; |
- } |
- |
- TypeMask _readableArrayTypeCache; |
- TypeMask get readableArrayType { |
- if (_readableArrayTypeCache == null) { |
- _readableArrayTypeCache = new TypeMask.nonNullSubclass(jsArrayClass, |
- compiler.world); |
- } |
- return _readableArrayTypeCache; |
- } |
- |
- TypeMask _mutableArrayTypeCache; |
- TypeMask get mutableArrayType { |
- if (_mutableArrayTypeCache == null) { |
- _mutableArrayTypeCache = new TypeMask.nonNullSubclass(jsMutableArrayClass, |
- compiler.world); |
- } |
- return _mutableArrayTypeCache; |
- } |
- |
- TypeMask _fixedArrayTypeCache; |
- TypeMask get fixedArrayType { |
- if (_fixedArrayTypeCache == null) { |
- _fixedArrayTypeCache = new TypeMask.nonNullExact(jsFixedArrayClass, |
- compiler.world); |
- } |
- return _fixedArrayTypeCache; |
- } |
- |
- TypeMask _extendableArrayTypeCache; |
- TypeMask get extendableArrayType { |
- if (_extendableArrayTypeCache == null) { |
- _extendableArrayTypeCache = |
- new TypeMask.nonNullExact(jsExtendableArrayClass, compiler.world); |
- } |
- return _extendableArrayTypeCache; |
- } |
- |
- TypeMask _nonNullTypeCache; |
- TypeMask get nonNullType { |
- if (_nonNullTypeCache == null) { |
- _nonNullTypeCache = |
- compiler.typesTask.dynamicType.nonNullable(); |
- } |
- return _nonNullTypeCache; |
- } |
- |
- /// Maps special classes to their implementation (JSXxx) class. |
- Map<ClassElement, ClassElement> implementationClasses; |
- |
- Element getNativeInterceptorMethod; |
- bool needToInitializeIsolateAffinityTag = false; |
- bool needToInitializeDispatchProperty = false; |
- |
- /// Holds the method "getIsolateAffinityTag" when dart:_js_helper has been |
- /// loaded. |
- FunctionElement getIsolateAffinityTagMarker; |
- |
- final Namer namer; |
- |
- /** |
- * Interface used to determine if an object has the JavaScript |
- * indexing behavior. The interface is only visible to specific |
- * libraries. |
- */ |
- ClassElement jsIndexingBehaviorInterface; |
- |
- /** |
- * A collection of selectors that must have a one shot interceptor |
- * generated. |
- */ |
- final Map<String, Selector> oneShotInterceptors; |
- |
- /** |
- * The members of instantiated interceptor classes: maps a member name to the |
- * list of members that have that name. This map is used by the codegen to |
- * know whether a send must be intercepted or not. |
- */ |
- final Map<String, Set<Element>> interceptedElements; |
- |
- /** |
- * The members of mixin classes that are mixed into an instantiated |
- * interceptor class. This is a cached subset of [interceptedElements]. |
- * |
- * Mixin methods are not specialized for the class they are mixed into. |
- * Methods mixed into intercepted classes thus always make use of the explicit |
- * receiver argument, even when mixed into non-interceptor classes. |
- * |
- * These members must be invoked with a correct explicit receiver even when |
- * the receiver is not an intercepted class. |
- */ |
- final Map<String, Set<Element>> interceptedMixinElements = |
- new Map<String, Set<Element>>(); |
- |
- /** |
- * A map of specialized versions of the [getInterceptorMethod]. |
- * Since [getInterceptorMethod] is a hot method at runtime, we're |
- * always specializing it based on the incoming type. The keys in |
- * the map are the names of these specialized versions. Note that |
- * the generic version that contains all possible type checks is |
- * also stored in this map. |
- */ |
- final Map<String, Set<ClassElement>> specializedGetInterceptors; |
- |
- /** |
- * Set of classes whose methods are intercepted. |
- */ |
- final Set<ClassElement> _interceptedClasses = new Set<ClassElement>(); |
- |
- /** |
- * Set of classes used as mixins on intercepted (native and primitive) |
- * classes. Methods on these classes might also be mixed in to regular Dart |
- * (unintercepted) classes. |
- */ |
- final Set<ClassElement> classesMixedIntoInterceptedClasses = |
- new Set<ClassElement>(); |
- |
- /** |
- * Set of classes whose `operator ==` methods handle `null` themselves. |
- */ |
- final Set<ClassElement> specialOperatorEqClasses = new Set<ClassElement>(); |
- |
- List<CompilerTask> get tasks { |
- return <CompilerTask>[builder, optimizer, generator, emitter]; |
- } |
- |
- final RuntimeTypes rti; |
- |
- /// Holds the method "disableTreeShaking" in js_mirrors when |
- /// dart:mirrors has been loaded. |
- FunctionElement disableTreeShakingMarker; |
- |
- /// Holds the method "preserveNames" in js_mirrors when |
- /// dart:mirrors has been loaded. |
- FunctionElement preserveNamesMarker; |
- |
- /// Holds the method "preserveMetadata" in js_mirrors when |
- /// dart:mirrors has been loaded. |
- FunctionElement preserveMetadataMarker; |
- |
- /// Holds the method "preserveUris" in js_mirrors when |
- /// dart:mirrors has been loaded. |
- FunctionElement preserveUrisMarker; |
- |
- /// Holds the method "preserveLibraryNames" in js_mirrors when |
- /// dart:mirrors has been loaded. |
- FunctionElement preserveLibraryNamesMarker; |
- |
- /// Holds the method "requiresPreamble" in _js_helper. |
- FunctionElement requiresPreambleMarker; |
- |
- /// True if a call to preserveMetadataMarker has been seen. This means that |
- /// metadata must be retained for dart:mirrors to work correctly. |
- bool mustRetainMetadata = false; |
- |
- /// True if any metadata has been retained. This is slightly different from |
- /// [mustRetainMetadata] and tells us if any metadata was retained. For |
- /// example, if [mustRetainMetadata] is true but there is no metadata in the |
- /// program, this variable will stil be false. |
- bool hasRetainedMetadata = false; |
- |
- /// True if a call to preserveUris has been seen and the preserve-uris flag |
- /// is set. |
- bool mustPreserveUris = false; |
- |
- /// True if a call to preserveLibraryNames has been seen. |
- bool mustRetainLibraryNames = false; |
- |
- /// True if a call to preserveNames has been seen. |
- bool mustPreserveNames = false; |
- |
- /// True if a call to disableTreeShaking has been seen. |
- bool isTreeShakingDisabled = false; |
- |
- /// True if there isn't sufficient @MirrorsUsed data. |
- bool hasInsufficientMirrorsUsed = false; |
- |
- /// True if a core-library function requires the preamble file to function. |
- bool requiresPreamble = false; |
- |
- /// True if the html library has been loaded. |
- bool htmlLibraryIsLoaded = false; |
- |
- /// List of constants from metadata. If metadata must be preserved, |
- /// these constants must be registered. |
- final List<Dependency> metadataConstants = <Dependency>[]; |
- |
- /// List of elements that the user has requested for reflection. |
- final Set<Element> targetsUsed = new Set<Element>(); |
- |
- /// List of annotations provided by user that indicate that the annotated |
- /// element must be retained. |
- final Set<Element> metaTargetsUsed = new Set<Element>(); |
- |
- /// Set of methods that are needed by reflection. Computed using |
- /// [computeMembersNeededForReflection] on first use. |
- Set<Element> _membersNeededForReflection = null; |
- Iterable<Element> get membersNeededForReflection { |
- assert(_membersNeededForReflection != null); |
- return _membersNeededForReflection; |
- } |
- |
- /// List of symbols that the user has requested for reflection. |
- final Set<String> symbolsUsed = new Set<String>(); |
- |
- /// List of elements that the backend may use. |
- final Set<Element> helpersUsed = new Set<Element>(); |
- |
- /// All the checked mode helpers. |
- static const checkedModeHelpers = CheckedModeHelper.helpers; |
- |
- // Checked mode helpers indexed by name. |
- Map<String, CheckedModeHelper> checkedModeHelperByName = |
- new Map<String, CheckedModeHelper>.fromIterable( |
- checkedModeHelpers, |
- key: (helper) => helper.name); |
- |
- TypeVariableHandler typeVariableHandler; |
- |
- /// Number of methods compiled before considering reflection. |
- int preMirrorsMethodCount = 0; |
- |
- /// Resolution and codegen support for generating table of interceptors and |
- /// constructors for custom elements. |
- CustomElementsAnalysis customElementsAnalysis; |
- |
- JavaScriptConstantTask constantCompilerTask; |
- |
- JavaScriptResolutionCallbacks resolutionCallbacks; |
- |
- JavaScriptBackend(Compiler compiler, bool generateSourceMap) |
- : namer = determineNamer(compiler), |
- oneShotInterceptors = new Map<String, Selector>(), |
- interceptedElements = new Map<String, Set<Element>>(), |
- rti = new RuntimeTypes(compiler), |
- specializedGetInterceptors = new Map<String, Set<ClassElement>>(), |
- super(compiler) { |
- emitter = new CodeEmitterTask(compiler, namer, generateSourceMap); |
- builder = new SsaBuilderTask(this); |
- optimizer = new SsaOptimizerTask(this); |
- generator = new SsaCodeGeneratorTask(this); |
- typeVariableHandler = new TypeVariableHandler(this); |
- customElementsAnalysis = new CustomElementsAnalysis(this); |
- constantCompilerTask = new JavaScriptConstantTask(compiler); |
- resolutionCallbacks = new JavaScriptResolutionCallbacks(this); |
- } |
- |
- ConstantSystem get constantSystem => constants.constantSystem; |
- |
- /// Returns constant environment for the JavaScript interpretation of the |
- /// constants. |
- JavaScriptConstantCompiler get constants { |
- return constantCompilerTask.jsConstantCompiler; |
- } |
- |
- // TODO(karlklose): Split into findHelperFunction and findHelperClass and |
- // add a check that the element has the expected kind. |
- Element findHelper(String name) => find(jsHelperLibrary, name); |
- Element findInterceptor(String name) => find(interceptorsLibrary, name); |
- |
- Element find(LibraryElement library, String name) { |
- Element element = library.findLocal(name); |
- assert(invariant(library, element != null, |
- message: "Element '$name' not found in '${library.canonicalUri}'.")); |
- return element; |
- } |
- |
- bool isForeign(Element element) => element.library == foreignLibrary; |
- |
- bool isBackendLibrary(LibraryElement library) { |
- return library == interceptorsLibrary || |
- library == jsHelperLibrary; |
- } |
- |
- static Namer determineNamer(Compiler compiler) { |
- return compiler.enableMinification ? |
- new MinifyNamer(compiler) : |
- new Namer(compiler); |
- } |
- |
- bool usedByBackend(Element element) { |
- if (element.isParameter |
- || element.isInitializingFormal |
- || element.isField) { |
- if (usedByBackend(element.enclosingElement)) return true; |
- } |
- return helpersUsed.contains(element.declaration); |
- } |
- |
- bool invokedReflectively(Element element) { |
- if (element.isParameter || element.isInitializingFormal) { |
- ParameterElement parameter = element; |
- if (invokedReflectively(parameter.functionDeclaration)) return true; |
- } |
- |
- if (element.isField) { |
- if (Elements.isStaticOrTopLevel(element) |
- && (element.isFinal || element.isConst)) { |
- return false; |
- } |
- } |
- |
- return isAccessibleByReflection(element.declaration); |
- } |
- |
- bool canBeUsedForGlobalOptimizations(Element element) { |
- return !usedByBackend(element) && !invokedReflectively(element); |
- } |
- |
- bool isInterceptorClass(ClassElement element) { |
- if (element == null) return false; |
- if (Elements.isNativeOrExtendsNative(element)) return true; |
- if (interceptedClasses.contains(element)) return true; |
- if (classesMixedIntoInterceptedClasses.contains(element)) return true; |
- return false; |
- } |
- |
- String registerOneShotInterceptor(Selector selector) { |
- Set<ClassElement> classes = getInterceptedClassesOn(selector.name); |
- String name = namer.getOneShotInterceptorName(selector, classes); |
- if (!oneShotInterceptors.containsKey(name)) { |
- registerSpecializedGetInterceptor(classes); |
- oneShotInterceptors[name] = selector; |
- } |
- return name; |
- } |
- |
- bool isInterceptedMethod(Element element) { |
- if (!element.isInstanceMember) return false; |
- if (element.isGenerativeConstructorBody) { |
- return Elements.isNativeOrExtendsNative(element.enclosingClass); |
- } |
- return interceptedElements[element.name] != null; |
- } |
- |
- bool fieldHasInterceptedGetter(Element element) { |
- assert(element.isField); |
- return interceptedElements[element.name] != null; |
- } |
- |
- bool fieldHasInterceptedSetter(Element element) { |
- assert(element.isField); |
- return interceptedElements[element.name] != null; |
- } |
- |
- bool isInterceptedName(String name) { |
- return interceptedElements[name] != null; |
- } |
- |
- bool isInterceptedSelector(Selector selector) { |
- return interceptedElements[selector.name] != null; |
- } |
- |
- /** |
- * Returns `true` iff [selector] matches an element defined in a class mixed |
- * into an intercepted class. These selectors are not eligible for the 'dummy |
- * explicit receiver' optimization. |
- */ |
- bool isInterceptedMixinSelector(Selector selector) { |
- Set<Element> elements = interceptedMixinElements.putIfAbsent( |
- selector.name, |
- () { |
- Set<Element> elements = interceptedElements[selector.name]; |
- if (elements == null) return null; |
- return elements |
- .where((element) => |
- classesMixedIntoInterceptedClasses.contains( |
- element.enclosingClass)) |
- .toSet(); |
- }); |
- |
- if (elements == null) return false; |
- if (elements.isEmpty) return false; |
- return elements.any((element) => selector.applies(element, compiler.world)); |
- } |
- |
- final Map<String, Set<ClassElement>> interceptedClassesCache = |
- new Map<String, Set<ClassElement>>(); |
- |
- /** |
- * Returns a set of interceptor classes that contain a member named |
- * [name]. Returns [:null:] if there is no class. |
- */ |
- Set<ClassElement> getInterceptedClassesOn(String name) { |
- Set<Element> intercepted = interceptedElements[name]; |
- if (intercepted == null) return null; |
- return interceptedClassesCache.putIfAbsent(name, () { |
- // Populate the cache by running through all the elements and |
- // determine if the given selector applies to them. |
- Set<ClassElement> result = new Set<ClassElement>(); |
- for (Element element in intercepted) { |
- ClassElement classElement = element.enclosingClass; |
- if (Elements.isNativeOrExtendsNative(classElement) |
- || interceptedClasses.contains(classElement)) { |
- result.add(classElement); |
- } |
- if (classesMixedIntoInterceptedClasses.contains(classElement)) { |
- Set<ClassElement> nativeSubclasses = |
- nativeSubclassesOfMixin(classElement); |
- if (nativeSubclasses != null) result.addAll(nativeSubclasses); |
- } |
- } |
- return result; |
- }); |
- } |
- |
- Set<ClassElement> nativeSubclassesOfMixin(ClassElement mixin) { |
- ClassWorld classWorld = compiler.world; |
- Iterable<MixinApplicationElement> uses = classWorld.mixinUsesOf(mixin); |
- Set<ClassElement> result = null; |
- for (MixinApplicationElement use in uses) { |
- Iterable<ClassElement> subclasses = classWorld.subclassesOf(use); |
- for (ClassElement subclass in subclasses) { |
- if (Elements.isNativeOrExtendsNative(subclass)) { |
- if (result == null) result = new Set<ClassElement>(); |
- result.add(subclass); |
- } |
- } |
- } |
- return result; |
- } |
- |
- bool operatorEqHandlesNullArgument(FunctionElement operatorEqfunction) { |
- return specialOperatorEqClasses.contains( |
- operatorEqfunction.enclosingClass); |
- } |
- |
- void validateInterceptorImplementsAllObjectMethods( |
- ClassElement interceptorClass) { |
- if (interceptorClass == null) return; |
- interceptorClass.ensureResolved(compiler); |
- compiler.objectClass.forEachMember((_, Element member) { |
- if (member.isGenerativeConstructor) return; |
- Element interceptorMember = interceptorClass.lookupMember(member.name); |
- // Interceptors must override all Object methods due to calling convention |
- // differences. |
- assert(interceptorMember.enclosingClass == interceptorClass); |
- }); |
- } |
- |
- void addInterceptorsForNativeClassMembers( |
- ClassElement cls, Enqueuer enqueuer) { |
- if (enqueuer.isResolutionQueue) { |
- cls.ensureResolved(compiler); |
- cls.forEachMember((ClassElement classElement, Element member) { |
- if (member.name == Compiler.CALL_OPERATOR_NAME) { |
- compiler.reportError( |
- member, |
- MessageKind.CALL_NOT_SUPPORTED_ON_NATIVE_CLASS); |
- return; |
- } |
- if (member.isSynthesized) return; |
- // All methods on [Object] are shadowed by [Interceptor]. |
- if (classElement == compiler.objectClass) return; |
- Set<Element> set = interceptedElements.putIfAbsent( |
- member.name, () => new Set<Element>()); |
- set.add(member); |
- }, |
- includeSuperAndInjectedMembers: true); |
- |
- // Walk superclass chain to find mixins. |
- for (; cls != null; cls = cls.superclass) { |
- if (cls.isMixinApplication) { |
- MixinApplicationElement mixinApplication = cls; |
- classesMixedIntoInterceptedClasses.add(mixinApplication.mixin); |
- } |
- } |
- } |
- } |
- |
- void addInterceptors(ClassElement cls, |
- Enqueuer enqueuer, |
- Registry registry) { |
- if (enqueuer.isResolutionQueue) { |
- _interceptedClasses.add(jsInterceptorClass); |
- _interceptedClasses.add(cls); |
- cls.ensureResolved(compiler); |
- cls.forEachMember((ClassElement classElement, Element member) { |
- // All methods on [Object] are shadowed by [Interceptor]. |
- if (classElement == compiler.objectClass) return; |
- Set<Element> set = interceptedElements.putIfAbsent( |
- member.name, () => new Set<Element>()); |
- set.add(member); |
- }, |
- includeSuperAndInjectedMembers: true); |
- } |
- enqueueClass(enqueuer, cls, registry); |
- } |
- |
- Set<ClassElement> get interceptedClasses { |
- assert(compiler.enqueuer.resolution.queueIsClosed); |
- return _interceptedClasses; |
- } |
- |
- void registerSpecializedGetInterceptor(Set<ClassElement> classes) { |
- String name = namer.getInterceptorName(getInterceptorMethod, classes); |
- if (classes.contains(jsInterceptorClass)) { |
- // We can't use a specialized [getInterceptorMethod], so we make |
- // sure we emit the one with all checks. |
- specializedGetInterceptors[name] = interceptedClasses; |
- } else { |
- specializedGetInterceptors[name] = classes; |
- } |
- } |
- |
- void registerCompileTimeConstant(ConstantValue constant, Registry registry) { |
- registerCompileTimeConstantInternal(constant, registry); |
- for (ConstantValue dependency in constant.getDependencies()) { |
- registerCompileTimeConstant(dependency, registry); |
- } |
- } |
- |
- void registerCompileTimeConstantInternal(ConstantValue constant, |
- Registry registry) { |
- DartType type = constant.computeType(compiler); |
- registerInstantiatedConstantType(type, registry); |
- |
- if (constant.isFunction) { |
- FunctionConstantValue function = constant; |
- registry.registerGetOfStaticFunction(function.element); |
- } else if (constant.isInterceptor) { |
- // An interceptor constant references the class's prototype chain. |
- InterceptorConstantValue interceptor = constant; |
- registerInstantiatedConstantType(interceptor.dispatchedType, registry); |
- } else if (constant.isType) { |
- enqueueInResolution(getCreateRuntimeType(), registry); |
- registry.registerInstantiation(typeImplementation.rawType); |
- } |
- } |
- |
- void registerInstantiatedConstantType(DartType type, Registry registry) { |
- DartType instantiatedType = |
- type.isFunctionType ? compiler.functionClass.rawType : type; |
- if (type is InterfaceType) { |
- registry.registerInstantiation(instantiatedType); |
- if (!type.treatAsRaw && classNeedsRti(type.element)) { |
- registry.registerStaticInvocation(getSetRuntimeTypeInfo()); |
- } |
- if (type.element == typeImplementation) { |
- // If we use a type literal in a constant, the compile time |
- // constant emitter will generate a call to the createRuntimeType |
- // helper so we register a use of that. |
- registry.registerStaticInvocation(getCreateRuntimeType()); |
- } |
- } |
- } |
- |
- void registerMetadataConstant(MetadataAnnotation metadata, |
- Element annotatedElement, |
- Registry registry) { |
- assert(registry.isForResolution); |
- ConstantValue constant = constants.getConstantForMetadata(metadata).value; |
- registerCompileTimeConstant(constant, registry); |
- metadataConstants.add(new Dependency(constant, annotatedElement)); |
- } |
- |
- void registerInstantiatedClass(ClassElement cls, |
- Enqueuer enqueuer, |
- Registry registry) { |
- if (!cls.typeVariables.isEmpty) { |
- typeVariableHandler.registerClassWithTypeVariables(cls); |
- } |
- |
- // Register any helper that will be needed by the backend. |
- if (enqueuer.isResolutionQueue) { |
- if (cls == compiler.intClass |
- || cls == compiler.doubleClass |
- || cls == compiler.numClass) { |
- // The backend will try to optimize number operations and use the |
- // `iae` helper directly. |
- enqueue(enqueuer, findHelper('iae'), registry); |
- } else if (cls == compiler.listClass |
- || cls == compiler.stringClass) { |
- // The backend will try to optimize array and string access and use the |
- // `ioore` and `iae` helpers directly. |
- enqueue(enqueuer, findHelper('ioore'), registry); |
- enqueue(enqueuer, findHelper('iae'), registry); |
- } else if (cls == compiler.functionClass) { |
- enqueueClass(enqueuer, closureClass, registry); |
- } else if (cls == compiler.mapClass) { |
- // The backend will use a literal list to initialize the entries |
- // of the map. |
- enqueueClass(enqueuer, compiler.listClass, registry); |
- enqueueClass(enqueuer, mapLiteralClass, registry); |
- // For map literals, the dependency between the implementation class |
- // and [Map] is not visible, so we have to add it manually. |
- rti.registerRtiDependency(mapLiteralClass, cls); |
- } else if (cls == boundClosureClass) { |
- // TODO(johnniwinther): Is this a noop? |
- enqueueClass(enqueuer, boundClosureClass, registry); |
- } else if (Elements.isNativeOrExtendsNative(cls)) { |
- enqueue(enqueuer, getNativeInterceptorMethod, registry); |
- enqueueClass(enqueuer, jsInterceptorClass, compiler.globalDependencies); |
- enqueueClass(enqueuer, jsPlainJavaScriptObjectClass, registry); |
- } else if (cls == mapLiteralClass) { |
- // For map literals, the dependency between the implementation class |
- // and [Map] is not visible, so we have to add it manually. |
- Element getFactory(String name, int arity) { |
- // The constructor is on the patch class, but dart2js unit tests don't |
- // have a patch class. |
- ClassElement implementation = cls.patch != null ? cls.patch : cls; |
- return implementation.lookupConstructor( |
- new Selector.callConstructor( |
- name, mapLiteralClass.library, arity), |
- (element) { |
- compiler.internalError(mapLiteralClass, |
- "Map literal class $mapLiteralClass missing " |
- "'$name' constructor" |
- " ${mapLiteralClass.constructors}"); |
- }); |
- } |
- mapLiteralConstructor = getFactory('_literal', 1); |
- mapLiteralConstructorEmpty = getFactory('_empty', 0); |
- enqueueInResolution(mapLiteralConstructor, registry); |
- enqueueInResolution(mapLiteralConstructorEmpty, registry); |
- } |
- } |
- if (cls == closureClass) { |
- enqueue(enqueuer, findHelper('closureFromTearOff'), registry); |
- } |
- ClassElement result = null; |
- if (cls == compiler.stringClass || cls == jsStringClass) { |
- addInterceptors(jsStringClass, enqueuer, registry); |
- } else if (cls == compiler.listClass || |
- cls == jsArrayClass || |
- cls == jsFixedArrayClass || |
- cls == jsExtendableArrayClass) { |
- addInterceptors(jsArrayClass, enqueuer, registry); |
- addInterceptors(jsMutableArrayClass, enqueuer, registry); |
- addInterceptors(jsFixedArrayClass, enqueuer, registry); |
- addInterceptors(jsExtendableArrayClass, enqueuer, registry); |
- } else if (cls == compiler.intClass || cls == jsIntClass) { |
- addInterceptors(jsIntClass, enqueuer, registry); |
- addInterceptors(jsPositiveIntClass, enqueuer, registry); |
- addInterceptors(jsUInt32Class, enqueuer, registry); |
- addInterceptors(jsUInt31Class, enqueuer, registry); |
- addInterceptors(jsNumberClass, enqueuer, registry); |
- } else if (cls == compiler.doubleClass || cls == jsDoubleClass) { |
- addInterceptors(jsDoubleClass, enqueuer, registry); |
- addInterceptors(jsNumberClass, enqueuer, registry); |
- } else if (cls == compiler.boolClass || cls == jsBoolClass) { |
- addInterceptors(jsBoolClass, enqueuer, registry); |
- } else if (cls == compiler.nullClass || cls == jsNullClass) { |
- addInterceptors(jsNullClass, enqueuer, registry); |
- } else if (cls == compiler.numClass || cls == jsNumberClass) { |
- addInterceptors(jsIntClass, enqueuer, registry); |
- addInterceptors(jsPositiveIntClass, enqueuer, registry); |
- addInterceptors(jsUInt32Class, enqueuer, registry); |
- addInterceptors(jsUInt31Class, enqueuer, registry); |
- addInterceptors(jsDoubleClass, enqueuer, registry); |
- addInterceptors(jsNumberClass, enqueuer, registry); |
- } else if (cls == jsPlainJavaScriptObjectClass) { |
- addInterceptors(jsPlainJavaScriptObjectClass, enqueuer, registry); |
- } else if (cls == jsUnknownJavaScriptObjectClass) { |
- addInterceptors(jsUnknownJavaScriptObjectClass, enqueuer, registry); |
- } else if (Elements.isNativeOrExtendsNative(cls)) { |
- addInterceptorsForNativeClassMembers(cls, enqueuer); |
- } else if (cls == jsIndexingBehaviorInterface) { |
- // These two helpers are used by the emitter and the codegen. |
- // Because we cannot enqueue elements at the time of emission, |
- // we make sure they are always generated. |
- enqueue(enqueuer, findHelper('isJsIndexable'), registry); |
- } |
- |
- customElementsAnalysis.registerInstantiatedClass(cls, enqueuer); |
- } |
- |
- void registerUseInterceptor(Enqueuer enqueuer) { |
- assert(!enqueuer.isResolutionQueue); |
- if (!enqueuer.nativeEnqueuer.hasInstantiatedNativeClasses()) return; |
- Registry registry = compiler.globalDependencies; |
- enqueue(enqueuer, getNativeInterceptorMethod, registry); |
- enqueueClass(enqueuer, jsPlainJavaScriptObjectClass, registry); |
- needToInitializeIsolateAffinityTag = true; |
- needToInitializeDispatchProperty = true; |
- } |
- |
- JavaScriptItemCompilationContext createItemCompilationContext() { |
- return new JavaScriptItemCompilationContext(); |
- } |
- |
- void enqueueHelpers(ResolutionEnqueuer world, Registry registry) { |
- assert(interceptorsLibrary != null); |
- // TODO(ngeoffray): Not enqueuing those two classes currently make |
- // the compiler potentially crash. However, any reasonable program |
- // will instantiate those two classes. |
- addInterceptors(jsBoolClass, world, registry); |
- addInterceptors(jsNullClass, world, registry); |
- if (compiler.enableTypeAssertions) { |
- // Unconditionally register the helper that checks if the |
- // expression in an if/while/for is a boolean. |
- // TODO(ngeoffray): Should we have the resolver register those instead? |
- Element e = findHelper('boolConversionCheck'); |
- if (e != null) enqueue(world, e, registry); |
- } |
- if (TRACE_CALLS) { |
- traceHelper = findHelper('traceHelper'); |
- assert(traceHelper != null); |
- enqueueInResolution(traceHelper, registry); |
- } |
- registerCheckedModeHelpers(registry); |
- } |
- |
- onResolutionComplete() { |
- super.onResolutionComplete(); |
- computeMembersNeededForReflection(); |
- rti.computeClassesNeedingRti(); |
- computeFunctionsToAlwaysInline(); |
- } |
- |
- void computeFunctionsToAlwaysInline() { |
- functionsToAlwaysInline = <FunctionElement>[]; |
- if (internalLibrary == null) return; |
- |
- // Try to find all functions intended to always inline. If their enclosing |
- // class is not resolved we skip the methods, but it is an error to mention |
- // a function or class that cannot be found. |
- for (String className in ALWAYS_INLINE.keys) { |
- ClassElement cls = find(internalLibrary, className); |
- if (cls.resolutionState != STATE_DONE) continue; |
- for (String functionName in ALWAYS_INLINE[className]) { |
- Element function = cls.lookupMember(functionName); |
- assert(invariant(cls, function is FunctionElement, |
- message: 'unable to find function $functionName in $className')); |
- functionsToAlwaysInline.add(function); |
- } |
- } |
- } |
- |
- void registerGetRuntimeTypeArgument(Registry registry) { |
- enqueueInResolution(getGetRuntimeTypeArgument(), registry); |
- enqueueInResolution(getGetTypeArgumentByIndex(), registry); |
- enqueueInResolution(getCopyTypeArguments(), registry); |
- } |
- |
- void registerCallMethodWithFreeTypeVariables( |
- Element callMethod, |
- Enqueuer enqueuer, |
- Registry registry) { |
- if (enqueuer.isResolutionQueue || methodNeedsRti(callMethod)) { |
- registerComputeSignature(enqueuer, registry); |
- } |
- } |
- |
- void registerClosureWithFreeTypeVariables( |
- Element closure, |
- Enqueuer enqueuer, |
- Registry registry) { |
- if (enqueuer.isResolutionQueue || methodNeedsRti(closure)) { |
- registerComputeSignature(enqueuer, registry); |
- } |
- } |
- |
- void registerBoundClosure(Enqueuer enqueuer) { |
- enqueuer.registerInstantiatedClass( |
- boundClosureClass, |
- // Precise dependency is not important here. |
- compiler.globalDependencies); |
- } |
- |
- void registerGetOfStaticFunction(Enqueuer enqueuer) { |
- enqueuer.registerInstantiatedClass(closureClass, |
- compiler.globalDependencies); |
- } |
- |
- void registerComputeSignature(Enqueuer enqueuer, Registry registry) { |
- // Calls to [:computeSignature:] are generated by the emitter and we |
- // therefore need to enqueue the used elements in the codegen enqueuer as |
- // well as in the resolution enqueuer. |
- enqueue(enqueuer, getSetRuntimeTypeInfo(), registry); |
- enqueue(enqueuer, getGetRuntimeTypeInfo(), registry); |
- enqueue(enqueuer, getComputeSignature(), registry); |
- enqueue(enqueuer, getGetRuntimeTypeArguments(), registry); |
- enqueueClass(enqueuer, compiler.listClass, registry); |
- } |
- |
- void registerRuntimeType(Enqueuer enqueuer, Registry registry) { |
- registerComputeSignature(enqueuer, registry); |
- enqueueInResolution(getSetRuntimeTypeInfo(), registry); |
- enqueueInResolution(getGetRuntimeTypeInfo(), registry); |
- registerGetRuntimeTypeArgument(registry); |
- enqueueClass(enqueuer, compiler.listClass, registry); |
- } |
- |
- void registerIsCheckForCodegen(DartType type, |
- Enqueuer world, |
- Registry registry) { |
- assert(!registry.isForResolution); |
- type = type.unalias(compiler); |
- enqueueClass(world, compiler.boolClass, registry); |
- bool inCheckedMode = compiler.enableTypeAssertions; |
- // [registerIsCheck] is also called for checked mode checks, so we |
- // need to register checked mode helpers. |
- if (inCheckedMode) { |
- // All helpers are added to resolution queue in enqueueHelpers. These |
- // calls to enqueueInResolution serve as assertions that the helper was |
- // in fact added. |
- // TODO(13155): Find a way to enqueue helpers lazily. |
- CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: false); |
- if (helper != null) { |
- enqueue(world, helper.getElement(compiler), registry); |
- } |
- // We also need the native variant of the check (for DOM types). |
- helper = getNativeCheckedModeHelper(type, typeCast: false); |
- if (helper != null) { |
- enqueue(world, helper.getElement(compiler), registry); |
- } |
- } |
- if (!type.treatAsRaw || type.containsTypeVariables) { |
- enqueueClass(world, compiler.listClass, registry); |
- } |
- if (type.element != null && type.element.isNative) { |
- // We will neeed to add the "$is" and "$as" properties on the |
- // JavaScript object prototype, so we make sure |
- // [:defineProperty:] is compiled. |
- enqueue(world, findHelper('defineProperty'), registry); |
- } |
- } |
- |
- void registerTypeVariableBoundsSubtypeCheck(DartType typeArgument, |
- DartType bound) { |
- rti.registerTypeVariableBoundsSubtypeCheck(typeArgument, bound); |
- } |
- |
- void registerCheckDeferredIsLoaded(Registry registry) { |
- enqueueInResolution(getCheckDeferredIsLoaded(), registry); |
- // Also register the types of the arguments passed to this method. |
- enqueueClass(compiler.enqueuer.resolution, compiler.stringClass, registry); |
- } |
- |
- void enableNoSuchMethod(Element context, Enqueuer world) { |
- enqueue(world, getCreateInvocationMirror(), compiler.globalDependencies); |
- world.registerInvocation(compiler.noSuchMethodSelector); |
- // TODO(tyoverby): Send the context element to DumpInfoTask to be |
- // blamed. |
- } |
- |
- void enableIsolateSupport(Enqueuer enqueuer) { |
- // TODO(floitsch): We should also ensure that the class IsolateMessage is |
- // instantiated. Currently, just enabling isolate support works. |
- if (compiler.mainFunction != null) { |
- // The JavaScript backend implements [Isolate.spawn] by looking up |
- // top-level functions by name. So all top-level function tear-off |
- // closures have a private name field. |
- // |
- // The JavaScript backend of [Isolate.spawnUri] uses the same internal |
- // implementation as [Isolate.spawn], and fails if it cannot look main up |
- // by name. |
- enqueuer.registerGetOfStaticFunction(compiler.mainFunction); |
- } |
- if (enqueuer.isResolutionQueue) { |
- for (String name in const [START_ROOT_ISOLATE, |
- '_currentIsolate', |
- '_callInIsolate']) { |
- Element element = find(isolateHelperLibrary, name); |
- enqueuer.addToWorkList(element); |
- compiler.globalDependencies.registerDependency(element); |
- } |
- } else { |
- enqueuer.addToWorkList(find(isolateHelperLibrary, START_ROOT_ISOLATE)); |
- } |
- } |
- |
- bool isAssertMethod(Element element) => element == assertMethod; |
- |
- void registerRequiredType(DartType type, Element enclosingElement) { |
- // If [argument] has type variables or is a type variable, this method |
- // registers a RTI dependency between the class where the type variable is |
- // defined (that is the enclosing class of the current element being |
- // resolved) and the class of [type]. If the class of [type] requires RTI, |
- // then the class of the type variable does too. |
- ClassElement contextClass = Types.getClassContext(type); |
- if (contextClass != null) { |
- assert(contextClass == enclosingElement.enclosingClass.declaration); |
- rti.registerRtiDependency(type.element, contextClass); |
- } |
- } |
- |
- void registerClassUsingVariableExpression(ClassElement cls) { |
- rti.classesUsingTypeVariableExpression.add(cls); |
- } |
- |
- bool classNeedsRti(ClassElement cls) { |
- return rti.classesNeedingRti.contains(cls.declaration) || |
- compiler.enabledRuntimeType; |
- } |
- |
- bool isDefaultNoSuchMethodImplementation(Element element) { |
- assert(element.name == Compiler.NO_SUCH_METHOD); |
- ClassElement classElement = element.enclosingClass; |
- return classElement == compiler.objectClass |
- || classElement == jsInterceptorClass |
- || classElement == jsNullClass; |
- } |
- |
- bool isDefaultEqualityImplementation(Element element) { |
- assert(element.name == '=='); |
- ClassElement classElement = element.enclosingClass; |
- return classElement == compiler.objectClass |
- || classElement == jsInterceptorClass |
- || classElement == jsNullClass; |
- } |
- |
- bool methodNeedsRti(FunctionElement function) { |
- return rti.methodsNeedingRti.contains(function) || |
- compiler.enabledRuntimeType; |
- } |
- |
- /// The backend must *always* call this method when enqueuing an |
- /// element. Calls done by the backend are not seen by global |
- /// optimizations, so they would make these optimizations unsound. |
- /// Therefore we need to collect the list of helpers the backend may |
- /// use. |
- Element registerBackendUse(Element element) { |
- if (element != null) { |
- helpersUsed.add(element.declaration); |
- if (element.isClass && element.isPatched) { |
- // Both declaration and implementation may declare fields, so we |
- // add both to the list of helpers. |
- helpersUsed.add(element.implementation); |
- } |
- } |
- return element; |
- } |
- |
- /// Enqueue [e] in [enqueuer]. |
- /// |
- /// This method calls [registerBackendUse]. |
- void enqueue(Enqueuer enqueuer, Element e, Registry registry) { |
- if (e == null) return; |
- registerBackendUse(e); |
- enqueuer.addToWorkList(e); |
- registry.registerDependency(e); |
- } |
- |
- /// Enqueue [e] in the resolution enqueuer. |
- /// |
- /// This method calls [registerBackendUse]. |
- void enqueueInResolution(Element e, Registry registry) { |
- if (e == null) return; |
- ResolutionEnqueuer enqueuer = compiler.enqueuer.resolution; |
- enqueue(enqueuer, e, registry); |
- } |
- |
- /// Register instantiation of [cls] in [enqueuer]. |
- /// |
- /// This method calls [registerBackendUse]. |
- void enqueueClass(Enqueuer enqueuer, Element cls, Registry registry) { |
- if (cls == null) return; |
- registerBackendUse(cls); |
- helpersUsed.add(cls.declaration); |
- if (cls.declaration != cls.implementation) { |
- helpersUsed.add(cls.implementation); |
- } |
- enqueuer.registerInstantiatedClass(cls, registry); |
- } |
- |
- void codegen(CodegenWorkItem work) { |
- Element element = work.element; |
- var kind = element.kind; |
- if (kind == ElementKind.TYPEDEF) return; |
- if (element.isConstructor && element.enclosingClass == jsNullClass) { |
- // Work around a problem compiling JSNull's constructor. |
- return; |
- } |
- if (kind.category == ElementCategory.VARIABLE) { |
- ConstantExpression initialValue = constants.getConstantForVariable(element); |
- if (initialValue != null) { |
- registerCompileTimeConstant(initialValue.value, work.registry); |
- constants.addCompileTimeConstantForEmission(initialValue.value); |
- // We don't need to generate code for static or top-level |
- // variables. For instance variables, we may need to generate |
- // the checked setter. |
- if (Elements.isStaticOrTopLevel(element)) return; |
- } else { |
- // If the constant-handler was not able to produce a result we have to |
- // go through the builder (below) to generate the lazy initializer for |
- // the static variable. |
- // We also need to register the use of the cyclic-error helper. |
- compiler.enqueuer.codegen.registerStaticUse(getCyclicThrowHelper()); |
- } |
- } |
- HGraph graph = builder.build(work); |
- optimizer.optimize(work, graph); |
- jsAst.Expression code = generator.generateCode(work, graph); |
- generatedCode[element] = code; |
- } |
- |
- native.NativeEnqueuer nativeResolutionEnqueuer(Enqueuer world) { |
- return new native.NativeResolutionEnqueuer(world, compiler); |
- } |
- |
- native.NativeEnqueuer nativeCodegenEnqueuer(Enqueuer world) { |
- return new native.NativeCodegenEnqueuer(world, compiler, emitter); |
- } |
- |
- ClassElement defaultSuperclass(ClassElement element) { |
- // Native classes inherit from Interceptor. |
- return element.isNative ? jsInterceptorClass : compiler.objectClass; |
- } |
- |
- /** |
- * Unit test hook that returns code of an element as a String. |
- * |
- * Invariant: [element] must be a declaration element. |
- */ |
- String assembleCode(Element element) { |
- assert(invariant(element, element.isDeclaration)); |
- return jsAst.prettyPrint(generatedCode[element], compiler).getText(); |
- } |
- |
- void assembleProgram() { |
- emitter.assembleProgram(); |
- int totalMethodCount = generatedCode.length; |
- if (totalMethodCount != preMirrorsMethodCount) { |
- int mirrorCount = totalMethodCount - preMirrorsMethodCount; |
- double percentage = (mirrorCount / totalMethodCount) * 100; |
- compiler.reportHint( |
- compiler.mainApp, MessageKind.MIRROR_BLOAT, |
- {'count': mirrorCount, |
- 'total': totalMethodCount, |
- 'percentage': percentage.round()}); |
- for (LibraryElement library in compiler.libraryLoader.libraries) { |
- if (library.isInternalLibrary) continue; |
- for (LibraryTag tag in library.tags) { |
- Import importTag = tag.asImport(); |
- if (importTag == null) continue; |
- LibraryElement importedLibrary = library.getLibraryFromTag(tag); |
- if (importedLibrary != compiler.mirrorsLibrary) continue; |
- MessageKind kind = |
- compiler.mirrorUsageAnalyzerTask.hasMirrorUsage(library) |
- ? MessageKind.MIRROR_IMPORT |
- : MessageKind.MIRROR_IMPORT_NO_USAGE; |
- compiler.withCurrentElement(library, () { |
- compiler.reportInfo(importTag, kind); |
- }); |
- } |
- } |
- } |
- } |
- |
- Element getDartClass(Element element) { |
- for (ClassElement dartClass in implementationClasses.keys) { |
- if (element == implementationClasses[dartClass]) { |
- return dartClass; |
- } |
- } |
- return element; |
- } |
- |
- /** |
- * Returns the checked mode helper that will be needed to do a type check/type |
- * cast on [type] at runtime. Note that this method is being called both by |
- * the resolver with interface types (int, String, ...), and by the SSA |
- * backend with implementation types (JSInt, JSString, ...). |
- */ |
- CheckedModeHelper getCheckedModeHelper(DartType type, {bool typeCast}) { |
- return getCheckedModeHelperInternal( |
- type, typeCast: typeCast, nativeCheckOnly: false); |
- } |
- |
- /** |
- * Returns the native checked mode helper that will be needed to do a type |
- * check/type cast on [type] at runtime. If no native helper exists for |
- * [type], [:null:] is returned. |
- */ |
- CheckedModeHelper getNativeCheckedModeHelper(DartType type, {bool typeCast}) { |
- return getCheckedModeHelperInternal( |
- type, typeCast: typeCast, nativeCheckOnly: true); |
- } |
- |
- /** |
- * Returns the checked mode helper for the type check/type cast for [type]. If |
- * [nativeCheckOnly] is [:true:], only names for native helpers are returned. |
- */ |
- CheckedModeHelper getCheckedModeHelperInternal(DartType type, |
- {bool typeCast, |
- bool nativeCheckOnly}) { |
- String name = getCheckedModeHelperNameInternal(type, |
- typeCast: typeCast, nativeCheckOnly: nativeCheckOnly); |
- if (name == null) return null; |
- CheckedModeHelper helper = checkedModeHelperByName[name]; |
- assert(helper != null); |
- return helper; |
- } |
- |
- String getCheckedModeHelperNameInternal(DartType type, |
- {bool typeCast, |
- bool nativeCheckOnly}) { |
- assert(type.kind != TypeKind.TYPEDEF); |
- if (type.isMalformed) { |
- // The same error is thrown for type test and type cast of a malformed |
- // type so we only need one check method. |
- return 'checkMalformedType'; |
- } |
- Element element = type.element; |
- bool nativeCheck = nativeCheckOnly || |
- emitter.nativeEmitter.requiresNativeIsCheck(element); |
- |
- // TODO(13955), TODO(9731). The test for non-primitive types should use an |
- // interceptor. The interceptor should be an argument to HTypeConversion so |
- // that it can be optimized by standard interceptor optimizations. |
- nativeCheck = true; |
- |
- if (type.isVoid) { |
- assert(!typeCast); // Cannot cast to void. |
- if (nativeCheckOnly) return null; |
- return 'voidTypeCheck'; |
- } else if (element == jsStringClass || element == compiler.stringClass) { |
- if (nativeCheckOnly) return null; |
- return typeCast |
- ? 'stringTypeCast' |
- : 'stringTypeCheck'; |
- } else if (element == jsDoubleClass || element == compiler.doubleClass) { |
- if (nativeCheckOnly) return null; |
- return typeCast |
- ? 'doubleTypeCast' |
- : 'doubleTypeCheck'; |
- } else if (element == jsNumberClass || element == compiler.numClass) { |
- if (nativeCheckOnly) return null; |
- return typeCast |
- ? 'numTypeCast' |
- : 'numTypeCheck'; |
- } else if (element == jsBoolClass || element == compiler.boolClass) { |
- if (nativeCheckOnly) return null; |
- return typeCast |
- ? 'boolTypeCast' |
- : 'boolTypeCheck'; |
- } else if (element == jsIntClass || element == compiler.intClass |
- || element == jsUInt32Class || element == jsUInt31Class |
- || element == jsPositiveIntClass) { |
- if (nativeCheckOnly) return null; |
- return typeCast |
- ? 'intTypeCast' |
- : 'intTypeCheck'; |
- } else if (Elements.isNumberOrStringSupertype(element, compiler)) { |
- if (nativeCheck) { |
- return typeCast |
- ? 'numberOrStringSuperNativeTypeCast' |
- : 'numberOrStringSuperNativeTypeCheck'; |
- } else { |
- return typeCast |
- ? 'numberOrStringSuperTypeCast' |
- : 'numberOrStringSuperTypeCheck'; |
- } |
- } else if (Elements.isStringOnlySupertype(element, compiler)) { |
- if (nativeCheck) { |
- return typeCast |
- ? 'stringSuperNativeTypeCast' |
- : 'stringSuperNativeTypeCheck'; |
- } else { |
- return typeCast |
- ? 'stringSuperTypeCast' |
- : 'stringSuperTypeCheck'; |
- } |
- } else if ((element == compiler.listClass || element == jsArrayClass) && |
- type.treatAsRaw) { |
- if (nativeCheckOnly) return null; |
- return typeCast |
- ? 'listTypeCast' |
- : 'listTypeCheck'; |
- } else { |
- if (Elements.isListSupertype(element, compiler)) { |
- if (nativeCheck) { |
- return typeCast |
- ? 'listSuperNativeTypeCast' |
- : 'listSuperNativeTypeCheck'; |
- } else { |
- return typeCast |
- ? 'listSuperTypeCast' |
- : 'listSuperTypeCheck'; |
- } |
- } else { |
- if (type.isInterfaceType && !type.treatAsRaw) { |
- return typeCast |
- ? 'subtypeCast' |
- : 'assertSubtype'; |
- } else if (type.isTypeVariable) { |
- return typeCast |
- ? 'subtypeOfRuntimeTypeCast' |
- : 'assertSubtypeOfRuntimeType'; |
- } else if (type.isFunctionType) { |
- return null; |
- } else { |
- if (nativeCheck) { |
- // TODO(karlklose): can we get rid of this branch when we use |
- // interceptors? |
- return typeCast |
- ? 'interceptedTypeCast' |
- : 'interceptedTypeCheck'; |
- } else { |
- return typeCast |
- ? 'propertyTypeCast' |
- : 'propertyTypeCheck'; |
- } |
- } |
- } |
- } |
- } |
- |
- void registerCheckedModeHelpers(Registry registry) { |
- // We register all the helpers in the resolution queue. |
- // TODO(13155): Find a way to register fewer helpers. |
- for (CheckedModeHelper helper in checkedModeHelpers) { |
- enqueueInResolution(helper.getElement(compiler), registry); |
- } |
- } |
- |
- /** |
- * Returns [:true:] if the checking of [type] is performed directly on the |
- * object and not on an interceptor. |
- */ |
- bool hasDirectCheckFor(DartType type) { |
- Element element = type.element; |
- return element == compiler.stringClass || |
- element == compiler.boolClass || |
- element == compiler.numClass || |
- element == compiler.intClass || |
- element == compiler.doubleClass || |
- element == jsArrayClass || |
- element == jsMutableArrayClass || |
- element == jsExtendableArrayClass || |
- element == jsFixedArrayClass; |
- } |
- |
- Element getExceptionUnwrapper() { |
- return findHelper('unwrapException'); |
- } |
- |
- Element getThrowRuntimeError() { |
- return findHelper('throwRuntimeError'); |
- } |
- |
- Element getThrowTypeError() { |
- return findHelper('throwTypeError'); |
- } |
- |
- Element getThrowAbstractClassInstantiationError() { |
- return findHelper('throwAbstractClassInstantiationError'); |
- } |
- |
- Element getStringInterpolationHelper() { |
- return findHelper('S'); |
- } |
- |
- Element getWrapExceptionHelper() { |
- return findHelper(r'wrapException'); |
- } |
- |
- Element getThrowExpressionHelper() { |
- return findHelper('throwExpression'); |
- } |
- |
- Element getClosureConverter() { |
- return findHelper('convertDartClosureToJS'); |
- } |
- |
- Element getTraceFromException() { |
- return findHelper('getTraceFromException'); |
- } |
- |
- Element getSetRuntimeTypeInfo() { |
- return findHelper('setRuntimeTypeInfo'); |
- } |
- |
- Element getGetRuntimeTypeInfo() { |
- return findHelper('getRuntimeTypeInfo'); |
- } |
- |
- Element getGetTypeArgumentByIndex() { |
- return findHelper('getTypeArgumentByIndex'); |
- } |
- |
- Element getCopyTypeArguments() { |
- return findHelper('copyTypeArguments'); |
- } |
- |
- Element getComputeSignature() { |
- return findHelper('computeSignature'); |
- } |
- |
- Element getGetRuntimeTypeArguments() { |
- return findHelper('getRuntimeTypeArguments'); |
- } |
- |
- Element getGetRuntimeTypeArgument() { |
- return findHelper('getRuntimeTypeArgument'); |
- } |
- |
- Element getRuntimeTypeToString() { |
- return findHelper('runtimeTypeToString'); |
- } |
- |
- Element getAssertIsSubtype() { |
- return findHelper('assertIsSubtype'); |
- } |
- |
- Element getCheckSubtype() { |
- return findHelper('checkSubtype'); |
- } |
- |
- Element getAssertSubtype() { |
- return findHelper('assertSubtype'); |
- } |
- |
- Element getCheckSubtypeOfRuntimeType() { |
- return findHelper('checkSubtypeOfRuntimeType'); |
- } |
- |
- Element getCheckDeferredIsLoaded() { |
- return findHelper('checkDeferredIsLoaded'); |
- } |
- |
- Element getAssertSubtypeOfRuntimeType() { |
- return findHelper('assertSubtypeOfRuntimeType'); |
- } |
- |
- Element getThrowNoSuchMethod() { |
- return findHelper('throwNoSuchMethod'); |
- } |
- |
- Element getCreateRuntimeType() { |
- return findHelper('createRuntimeType'); |
- } |
- |
- Element getFallThroughError() { |
- return findHelper("getFallThroughError"); |
- } |
- |
- Element getCreateInvocationMirror() { |
- return findHelper(Compiler.CREATE_INVOCATION_MIRROR); |
- } |
- |
- Element getCyclicThrowHelper() { |
- return findHelper("throwCyclicInit"); |
- } |
- |
- bool isNullImplementation(ClassElement cls) { |
- return cls == jsNullClass; |
- } |
- |
- ClassElement get intImplementation => jsIntClass; |
- ClassElement get uint32Implementation => jsUInt32Class; |
- ClassElement get uint31Implementation => jsUInt31Class; |
- ClassElement get positiveIntImplementation => jsPositiveIntClass; |
- ClassElement get doubleImplementation => jsDoubleClass; |
- ClassElement get numImplementation => jsNumberClass; |
- ClassElement get stringImplementation => jsStringClass; |
- ClassElement get listImplementation => jsArrayClass; |
- ClassElement get constListImplementation => jsArrayClass; |
- ClassElement get fixedListImplementation => jsFixedArrayClass; |
- ClassElement get growableListImplementation => jsExtendableArrayClass; |
- ClassElement get mapImplementation => mapLiteralClass; |
- ClassElement get constMapImplementation => constMapLiteralClass; |
- ClassElement get typeImplementation => typeLiteralClass; |
- ClassElement get boolImplementation => jsBoolClass; |
- ClassElement get nullImplementation => jsNullClass; |
- |
- void registerStaticUse(Element element, Enqueuer enqueuer) { |
- if (element == disableTreeShakingMarker) { |
- compiler.disableTypeInferenceForMirrors = true; |
- isTreeShakingDisabled = true; |
- typeVariableHandler.onTreeShakingDisabled(enqueuer); |
- } else if (element == preserveNamesMarker) { |
- mustPreserveNames = true; |
- } else if (element == preserveMetadataMarker) { |
- mustRetainMetadata = true; |
- } else if (element == preserveUrisMarker) { |
- if (compiler.preserveUris) mustPreserveUris = true; |
- } else if (element == preserveLibraryNamesMarker) { |
- mustRetainLibraryNames = true; |
- } else if (element == getIsolateAffinityTagMarker) { |
- needToInitializeIsolateAffinityTag = true; |
- } else if (element.isDeferredLoaderGetter) { |
- // TODO(sigurdm): Create a function registerLoadLibraryAccess. |
- if (compiler.loadLibraryFunction == null) { |
- compiler.loadLibraryFunction = |
- findHelper("_loadLibraryWrapper"); |
- enqueueInResolution(compiler.loadLibraryFunction, |
- compiler.globalDependencies); |
- } |
- } else if (element == requiresPreambleMarker) { |
- requiresPreamble = true; |
- } |
- customElementsAnalysis.registerStaticUse(element, enqueuer); |
- } |
- |
- /// Called when [:const Symbol(name):] is seen. |
- void registerConstSymbol(String name, Registry registry) { |
- symbolsUsed.add(name); |
- if (name.endsWith('=')) { |
- symbolsUsed.add(name.substring(0, name.length - 1)); |
- } |
- } |
- |
- /// Called when [:new Symbol(...):] is seen. |
- void registerNewSymbol(Registry registry) { |
- } |
- |
- /// Should [element] (a getter) that would normally not be generated due to |
- /// treeshaking be retained for reflection? |
- bool shouldRetainGetter(Element element) { |
- return isTreeShakingDisabled && isAccessibleByReflection(element); |
- } |
- |
- /// Should [element] (a setter) hat would normally not be generated due to |
- /// treeshaking be retained for reflection? |
- bool shouldRetainSetter(Element element) { |
- return isTreeShakingDisabled && isAccessibleByReflection(element); |
- } |
- |
- /// Should [name] be retained for reflection? |
- bool shouldRetainName(String name) { |
- if (hasInsufficientMirrorsUsed) return mustPreserveNames; |
- if (name == '') return false; |
- return symbolsUsed.contains(name); |
- } |
- |
- bool retainMetadataOf(Element element) { |
- if (mustRetainMetadata) hasRetainedMetadata = true; |
- if (mustRetainMetadata && referencedFromMirrorSystem(element)) { |
- for (MetadataAnnotation metadata in element.metadata) { |
- metadata.ensureResolved(compiler); |
- ConstantValue constant = |
- constants.getConstantForMetadata(metadata).value; |
- constants.addCompileTimeConstantForEmission(constant); |
- } |
- return true; |
- } |
- return false; |
- } |
- |
- void onLibraryCreated(LibraryElement library) { |
- Uri uri = library.canonicalUri; |
- if (uri == DART_JS_HELPER) { |
- jsHelperLibrary = library; |
- } else if (uri == DART_INTERNAL) { |
- internalLibrary = library; |
- } else if (uri == DART_INTERCEPTORS) { |
- interceptorsLibrary = library; |
- } else if (uri == DART_FOREIGN_HELPER) { |
- foreignLibrary = library; |
- } else if (uri == DART_ISOLATE_HELPER) { |
- isolateHelperLibrary = library; |
- } |
- } |
- |
- void initializeHelperClasses() { |
- final List missingHelperClasses = []; |
- ClassElement lookupHelperClass(String name) { |
- ClassElement result = findHelper(name); |
- if (result == null) { |
- missingHelperClasses.add(name); |
- } |
- return result; |
- } |
- jsInvocationMirrorClass = lookupHelperClass('JSInvocationMirror'); |
- boundClosureClass = lookupHelperClass('BoundClosure'); |
- closureClass = lookupHelperClass('Closure'); |
- if (!missingHelperClasses.isEmpty) { |
- compiler.internalError(jsHelperLibrary, |
- 'dart:_js_helper library does not contain required classes: ' |
- '$missingHelperClasses'); |
- } |
- } |
- |
- Future onLibraryScanned(LibraryElement library, LibraryLoader loader) { |
- return super.onLibraryScanned(library, loader).then((_) { |
- Uri uri = library.canonicalUri; |
- |
- VariableElement findVariable(String name) { |
- return find(library, name); |
- } |
- |
- FunctionElement findMethod(String name) { |
- return find(library, name); |
- } |
- |
- ClassElement findClass(String name) { |
- return find(library, name); |
- } |
- |
- if (uri == DART_INTERCEPTORS) { |
- getInterceptorMethod = findMethod('getInterceptor'); |
- interceptedNames = findVariable('interceptedNames'); |
- mapTypeToInterceptor = findVariable('mapTypeToInterceptor'); |
- getNativeInterceptorMethod = findMethod('getNativeInterceptor'); |
- |
- List<ClassElement> classes = [ |
- jsInterceptorClass = findClass('Interceptor'), |
- jsStringClass = findClass('JSString'), |
- jsArrayClass = findClass('JSArray'), |
- // The int class must be before the double class, because the |
- // emitter relies on this list for the order of type checks. |
- jsIntClass = findClass('JSInt'), |
- jsPositiveIntClass = findClass('JSPositiveInt'), |
- jsUInt32Class = findClass('JSUInt32'), |
- jsUInt31Class = findClass('JSUInt31'), |
- jsDoubleClass = findClass('JSDouble'), |
- jsNumberClass = findClass('JSNumber'), |
- jsNullClass = findClass('JSNull'), |
- jsBoolClass = findClass('JSBool'), |
- jsMutableArrayClass = findClass('JSMutableArray'), |
- jsFixedArrayClass = findClass('JSFixedArray'), |
- jsExtendableArrayClass = findClass('JSExtendableArray'), |
- jsPlainJavaScriptObjectClass = findClass('PlainJavaScriptObject'), |
- jsUnknownJavaScriptObjectClass = findClass('UnknownJavaScriptObject'), |
- ]; |
- |
- jsIndexableClass = findClass('JSIndexable'); |
- jsMutableIndexableClass = findClass('JSMutableIndexable'); |
- } else if (uri == DART_JS_HELPER) { |
- initializeHelperClasses(); |
- assertMethod = findHelper('assertHelper'); |
- |
- typeLiteralClass = findClass('TypeImpl'); |
- constMapLiteralClass = findClass('ConstantMap'); |
- typeVariableClass = findClass('TypeVariable'); |
- |
- jsIndexingBehaviorInterface = findClass('JavaScriptIndexingBehavior'); |
- |
- noSideEffectsClass = findClass('NoSideEffects'); |
- noThrowsClass = findClass('NoThrows'); |
- noInlineClass = findClass('NoInline'); |
- irRepresentationClass = findClass('IrRepresentation'); |
- |
- getIsolateAffinityTagMarker = findMethod('getIsolateAffinityTag'); |
- |
- requiresPreambleMarker = findMethod('requiresPreamble'); |
- } else if (uri == DART_JS_MIRRORS) { |
- disableTreeShakingMarker = find(library, 'disableTreeShaking'); |
- preserveMetadataMarker = find(library, 'preserveMetadata'); |
- preserveUrisMarker = find(library, 'preserveUris'); |
- preserveLibraryNamesMarker = find(library, 'preserveLibraryNames'); |
- } else if (uri == DART_JS_NAMES) { |
- preserveNamesMarker = find(library, 'preserveNames'); |
- } else if (uri == DART_HTML) { |
- htmlLibraryIsLoaded = true; |
- } |
- }); |
- } |
- |
- Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) { |
- if (!loadedLibraries.containsKey(Compiler.DART_CORE)) { |
- return new Future.value(); |
- } |
- |
- assert(loadedLibraries.containsKey(Compiler.DART_CORE)); |
- assert(loadedLibraries.containsKey(DART_INTERCEPTORS)); |
- assert(loadedLibraries.containsKey(DART_JS_HELPER)); |
- |
- if (jsInvocationMirrorClass != null) { |
- jsInvocationMirrorClass.ensureResolved(compiler); |
- invokeOnMethod = jsInvocationMirrorClass.lookupLocalMember(INVOKE_ON); |
- } |
- |
- // [LinkedHashMap] is reexported from dart:collection and can therefore not |
- // be loaded from dart:core in [onLibraryScanned]. |
- mapLiteralClass = compiler.coreLibrary.find('LinkedHashMap'); |
- assert(invariant(compiler.coreLibrary, mapLiteralClass != null, |
- message: "Element 'LinkedHashMap' not found in 'dart:core'.")); |
- |
- implementationClasses = <ClassElement, ClassElement>{}; |
- implementationClasses[compiler.intClass] = jsIntClass; |
- implementationClasses[compiler.boolClass] = jsBoolClass; |
- implementationClasses[compiler.numClass] = jsNumberClass; |
- implementationClasses[compiler.doubleClass] = jsDoubleClass; |
- implementationClasses[compiler.stringClass] = jsStringClass; |
- implementationClasses[compiler.listClass] = jsArrayClass; |
- implementationClasses[compiler.nullClass] = jsNullClass; |
- |
- // These methods are overwritten with generated versions. |
- inlineCache.markAsNonInlinable(getInterceptorMethod, insideLoop: true); |
- |
- // TODO(kasperl): Some tests do not define the special JSArray |
- // subclasses, so we check to see if they are defined before |
- // trying to resolve them. |
- if (jsFixedArrayClass != null) { |
- jsFixedArrayClass.ensureResolved(compiler); |
- } |
- if (jsExtendableArrayClass != null) { |
- jsExtendableArrayClass.ensureResolved(compiler); |
- } |
- |
- jsIndexableClass.ensureResolved(compiler); |
- jsIndexableLength = compiler.lookupElementIn( |
- jsIndexableClass, 'length'); |
- if (jsIndexableLength != null && jsIndexableLength.isAbstractField) { |
- AbstractFieldElement element = jsIndexableLength; |
- jsIndexableLength = element.getter; |
- } |
- |
- jsArrayClass.ensureResolved(compiler); |
- jsArrayTypedConstructor = compiler.lookupElementIn(jsArrayClass, 'typed'); |
- jsArrayRemoveLast = compiler.lookupElementIn(jsArrayClass, 'removeLast'); |
- jsArrayAdd = compiler.lookupElementIn(jsArrayClass, 'add'); |
- |
- jsStringClass.ensureResolved(compiler); |
- jsStringSplit = compiler.lookupElementIn(jsStringClass, 'split'); |
- jsStringOperatorAdd = compiler.lookupElementIn(jsStringClass, '+'); |
- jsStringToString = compiler.lookupElementIn(jsStringClass, 'toString'); |
- |
- objectEquals = compiler.lookupElementIn(compiler.objectClass, '=='); |
- |
- specialOperatorEqClasses |
- ..add(compiler.objectClass) |
- ..add(jsInterceptorClass) |
- ..add(jsNullClass); |
- |
- validateInterceptorImplementsAllObjectMethods(jsInterceptorClass); |
- // The null-interceptor must also implement *all* methods. |
- validateInterceptorImplementsAllObjectMethods(jsNullClass); |
- |
- return new Future.value(); |
- } |
- |
- void registerMirrorUsage(Set<String> symbols, |
- Set<Element> targets, |
- Set<Element> metaTargets) { |
- if (symbols == null && targets == null && metaTargets == null) { |
- // The user didn't specify anything, or there are imports of |
- // 'dart:mirrors' without @MirrorsUsed. |
- hasInsufficientMirrorsUsed = true; |
- return; |
- } |
- if (symbols != null) symbolsUsed.addAll(symbols); |
- if (targets != null) { |
- for (Element target in targets) { |
- if (target.isAbstractField) { |
- AbstractFieldElement field = target; |
- targetsUsed.add(field.getter); |
- targetsUsed.add(field.setter); |
- } else { |
- targetsUsed.add(target); |
- } |
- } |
- } |
- if (metaTargets != null) metaTargetsUsed.addAll(metaTargets); |
- } |
- |
- /** |
- * Returns `true` if [element] can be accessed through reflection, that is, |
- * is in the set of elements covered by a `MirrorsUsed` annotation. |
- * |
- * This property is used to tag emitted elements with a marker which is |
- * checked by the runtime system to throw an exception if an element is |
- * accessed (invoked, get, set) that is not accessible for the reflective |
- * system. |
- */ |
- bool isAccessibleByReflection(Element element) { |
- if (element.isClass) { |
- element = getDartClass(element); |
- } |
- return membersNeededForReflection.contains(element); |
- } |
- |
- /** |
- * Returns true if the element has to be resolved due to a mirrorsUsed |
- * annotation. If we have insufficient mirrors used annotations, we only |
- * keep additonal elements if treeshaking has been disabled. |
- */ |
- bool requiredByMirrorSystem(Element element) { |
- return hasInsufficientMirrorsUsed && isTreeShakingDisabled || |
- matchesMirrorsMetaTarget(element) || |
- targetsUsed.contains(element); |
- } |
- |
- /** |
- * Returns true if the element matches a mirrorsUsed annotation. If |
- * we have insufficient mirrorsUsed information, this returns true for |
- * all elements, as they might all be potentially referenced. |
- */ |
- bool referencedFromMirrorSystem(Element element, [recursive = true]) { |
- Element enclosing = recursive ? element.enclosingElement : null; |
- |
- return hasInsufficientMirrorsUsed || |
- matchesMirrorsMetaTarget(element) || |
- targetsUsed.contains(element) || |
- (enclosing != null && referencedFromMirrorSystem(enclosing)); |
- } |
- |
- /** |
- * Returns `true` if the element is needed because it has an annotation |
- * of a type that is used as a meta target for reflection. |
- */ |
- bool matchesMirrorsMetaTarget(Element element) { |
- if (metaTargetsUsed.isEmpty) return false; |
- for (Link link = element.metadata; !link.isEmpty; link = link.tail) { |
- MetadataAnnotation metadata = link.head; |
- // TODO(kasperl): It would be nice if we didn't have to resolve |
- // all metadata but only stuff that potentially would match one |
- // of the used meta targets. |
- metadata.ensureResolved(compiler); |
- ConstantValue value = metadata.constant.value; |
- if (value == null) continue; |
- DartType type = value.computeType(compiler); |
- if (metaTargetsUsed.contains(type.element)) return true; |
- } |
- return false; |
- } |
- |
- /** |
- * Visits all classes and computes whether its members are needed for |
- * reflection. |
- * |
- * We have to precompute this set as we cannot easily answer the need for |
- * reflection locally when looking at the member: We lack the information by |
- * which classes a member is inherited. Called after resolution is complete. |
- * |
- * We filter out private libraries here, as their elements should not |
- * be visible by reflection unless some other interfaces makes them |
- * accessible. |
- */ |
- computeMembersNeededForReflection() { |
- if (_membersNeededForReflection != null) return; |
- if (compiler.mirrorsLibrary == null) { |
- _membersNeededForReflection = const ImmutableEmptySet<Element>(); |
- return; |
- } |
- // Compute a mapping from class to the closures it contains, so we |
- // can include the correct ones when including the class. |
- Map<ClassElement, List<LocalFunctionElement>> closureMap = |
- new Map<ClassElement, List<LocalFunctionElement>>(); |
- for (LocalFunctionElement closure in compiler.resolverWorld.allClosures) { |
- closureMap.putIfAbsent(closure.enclosingClass, () => []).add(closure); |
- } |
- bool foundClosure = false; |
- Set<Element> reflectableMembers = new Set<Element>(); |
- ResolutionEnqueuer resolution = compiler.enqueuer.resolution; |
- for (ClassElement cls in resolution.universe.directlyInstantiatedClasses) { |
- // Do not process internal classes. |
- if (cls.library.isInternalLibrary || cls.isInjected) continue; |
- if (referencedFromMirrorSystem(cls)) { |
- Set<Name> memberNames = new Set<Name>(); |
- // 1) the class (should be resolved) |
- assert(invariant(cls, cls.isResolved)); |
- reflectableMembers.add(cls); |
- // 2) its constructors (if resolved) |
- cls.constructors.forEach((Element constructor) { |
- if (resolution.hasBeenResolved(constructor)) { |
- reflectableMembers.add(constructor); |
- } |
- }); |
- // 3) all members, including fields via getter/setters (if resolved) |
- cls.forEachClassMember((Member member) { |
- if (resolution.hasBeenResolved(member.element)) { |
- memberNames.add(member.name); |
- reflectableMembers.add(member.element); |
- } |
- }); |
- // 4) all overriding members of subclasses/subtypes (should be resolved) |
- if (compiler.world.hasAnySubtype(cls)) { |
- for (ClassElement subcls in compiler.world.subtypesOf(cls)) { |
- subcls.forEachClassMember((Member member) { |
- if (memberNames.contains(member.name)) { |
- // TODO(20993): find out why this assertion fails. |
- // assert(invariant(member.element, |
- // resolution.hasBeenResolved(member.element))); |
- if (resolution.hasBeenResolved(member.element)) { |
- reflectableMembers.add(member.element); |
- } |
- } |
- }); |
- } |
- } |
- // 5) all its closures |
- List<LocalFunctionElement> closures = closureMap[cls]; |
- if (closures != null) { |
- reflectableMembers.addAll(closures); |
- foundClosure = true; |
- } |
- } else { |
- // check members themselves |
- cls.constructors.forEach((ConstructorElement element) { |
- if (!resolution.hasBeenResolved(element)) return; |
- if (referencedFromMirrorSystem(element, false)) { |
- reflectableMembers.add(element); |
- } |
- }); |
- cls.forEachClassMember((Member member) { |
- if (!resolution.hasBeenResolved(member.element)) return; |
- if (referencedFromMirrorSystem(member.element, false)) { |
- reflectableMembers.add(member.element); |
- } |
- }); |
- // Also add in closures. Those might be reflectable is their enclosing |
- // member is. |
- List<LocalFunctionElement> closures = closureMap[cls]; |
- if (closures != null) { |
- for (LocalFunctionElement closure in closures) { |
- if (referencedFromMirrorSystem(closure.memberContext, false)) { |
- reflectableMembers.add(closure); |
- foundClosure = true; |
- } |
- } |
- } |
- } |
- } |
- // We also need top-level non-class elements like static functions and |
- // global fields. We use the resolution queue to decide which elements are |
- // part of the live world. |
- for (LibraryElement lib in compiler.libraryLoader.libraries) { |
- if (lib.isInternalLibrary) continue; |
- lib.forEachLocalMember((Element member) { |
- if (!member.isClass && |
- resolution.hasBeenResolved(member) && |
- referencedFromMirrorSystem(member)) { |
- reflectableMembers.add(member); |
- } |
- }); |
- } |
- // And closures inside top-level elements that do not have a surrounding |
- // class. These will be in the [:null:] bucket of the [closureMap]. |
- if (closureMap.containsKey(null)) { |
- for (Element closure in closureMap[null]) { |
- if (referencedFromMirrorSystem(closure)) { |
- reflectableMembers.add(closure); |
- foundClosure = true; |
- } |
- } |
- } |
- // As we do not think about closures as classes, yet, we have to make sure |
- // their superclasses are available for reflection manually. |
- if (foundClosure) { |
- reflectableMembers.add(closureClass); |
- } |
- Set<Element> closurizedMembers = compiler.resolverWorld.closurizedMembers; |
- if (closurizedMembers.any(reflectableMembers.contains)) { |
- reflectableMembers.add(boundClosureClass); |
- } |
- // Add typedefs. |
- reflectableMembers |
- .addAll(compiler.world.allTypedefs.where(referencedFromMirrorSystem)); |
- // Register all symbols of reflectable elements |
- for (Element element in reflectableMembers) { |
- symbolsUsed.add(element.name); |
- } |
- _membersNeededForReflection = reflectableMembers; |
- } |
- |
- // TODO(20791): compute closure classes after resolution and move this code to |
- // [computeMembersNeededForReflection]. |
- void maybeMarkClosureAsNeededForReflection( |
- ClosureClassElement globalizedElement, |
- FunctionElement callFunction, |
- FunctionElement function) { |
- if (!_membersNeededForReflection.contains(function)) return; |
- _membersNeededForReflection.add(callFunction); |
- _membersNeededForReflection.add(globalizedElement); |
- } |
- |
- jsAst.Call generateIsJsIndexableCall(jsAst.Expression use1, |
- jsAst.Expression use2) { |
- String dispatchPropertyName = embeddedNames.DISPATCH_PROPERTY_NAME; |
- jsAst.Expression dispatchProperty = |
- emitter.generateEmbeddedGlobalAccess(dispatchPropertyName); |
- |
- // We pass the dispatch property record to the isJsIndexable |
- // helper rather than reading it inside the helper to increase the |
- // chance of making the dispatch record access monomorphic. |
- jsAst.PropertyAccess record = |
- new jsAst.PropertyAccess(use2, dispatchProperty); |
- |
- List<jsAst.Expression> arguments = <jsAst.Expression>[use1, record]; |
- FunctionElement helper = findHelper('isJsIndexable'); |
- jsAst.Expression helperExpression = namer.elementAccess(helper); |
- return new jsAst.Call(helperExpression, arguments); |
- } |
- |
- bool isTypedArray(TypeMask mask) { |
- // Just checking for [:TypedData:] is not sufficient, as it is an |
- // abstract class any user-defined class can implement. So we also |
- // check for the interface [JavaScriptIndexingBehavior]. |
- return |
- compiler.typedDataClass != null && |
- compiler.world.isInstantiated(compiler.typedDataClass) && |
- mask.satisfies(compiler.typedDataClass, compiler.world) && |
- mask.satisfies(jsIndexingBehaviorInterface, compiler.world); |
- } |
- |
- bool couldBeTypedArray(TypeMask mask) { |
- bool intersects(TypeMask type1, TypeMask type2) => |
- !type1.intersection(type2, compiler.world).isEmpty; |
- // TODO(herhut): Maybe cache the TypeMask for typedDataClass and |
- // jsIndexingBehaviourInterface. |
- return |
- compiler.typedDataClass != null && |
- compiler.world.isInstantiated(compiler.typedDataClass) && |
- intersects(mask, |
- new TypeMask.subtype(compiler.typedDataClass, compiler.world)) && |
- intersects(mask, |
- new TypeMask.subtype(jsIndexingBehaviorInterface, compiler.world)); |
- } |
- |
- /// Returns all static fields that are referenced through [targetsUsed]. |
- /// If the target is a library or class all nested static fields are |
- /// included too. |
- Iterable<Element> _findStaticFieldTargets() { |
- List staticFields = []; |
- |
- void addFieldsInContainer(ScopeContainerElement container) { |
- container.forEachLocalMember((Element member) { |
- if (!member.isInstanceMember && member.isField) { |
- staticFields.add(member); |
- } else if (member.isClass) { |
- addFieldsInContainer(member); |
- } |
- }); |
- } |
- |
- for (Element target in targetsUsed) { |
- if (target == null) continue; |
- if (target.isField) { |
- staticFields.add(target); |
- } else if (target.isLibrary || target.isClass) { |
- addFieldsInContainer(target); |
- } |
- } |
- return staticFields; |
- } |
- |
- /// Called when [enqueuer] is empty, but before it is closed. |
- bool onQueueEmpty(Enqueuer enqueuer, Iterable<ClassElement> recentClasses) { |
- // Add elements referenced only via custom elements. Return early if any |
- // elements are added to avoid counting the elements as due to mirrors. |
- customElementsAnalysis.onQueueEmpty(enqueuer); |
- if (!enqueuer.queueIsEmpty) return false; |
- |
- if (!enqueuer.isResolutionQueue && preMirrorsMethodCount == 0) { |
- preMirrorsMethodCount = generatedCode.length; |
- } |
- |
- if (isTreeShakingDisabled) { |
- enqueuer.enqueueReflectiveElements(recentClasses); |
- } else if (!targetsUsed.isEmpty && enqueuer.isResolutionQueue) { |
- // Add all static elements (not classes) that have been requested for |
- // reflection. If there is no mirror-usage these are probably not |
- // necessary, but the backend relies on them being resolved. |
- enqueuer.enqueueReflectiveStaticFields(_findStaticFieldTargets()); |
- } |
- |
- if (mustPreserveNames) compiler.log('Preserving names.'); |
- |
- if (mustRetainMetadata) { |
- compiler.log('Retaining metadata.'); |
- |
- compiler.libraryLoader.libraries.forEach(retainMetadataOf); |
- if (!enqueuer.isResolutionQueue) { |
- for (Dependency dependency in metadataConstants) { |
- registerCompileTimeConstant( |
- dependency.constant, |
- new CodegenRegistry(compiler, |
- dependency.annotatedElement.analyzableElement.treeElements)); |
- } |
- metadataConstants.clear(); |
- } |
- } |
- return true; |
- } |
- |
- void onElementResolved(Element element, TreeElements elements) { |
- LibraryElement library = element.library; |
- if (!library.isPlatformLibrary && !library.canUseNative) return; |
- bool hasNoInline = false; |
- bool hasNoThrows = false; |
- bool hasNoSideEffects = false; |
- for (MetadataAnnotation metadata in element.metadata) { |
- metadata.ensureResolved(compiler); |
- if (!metadata.constant.value.isConstructedObject) continue; |
- ObjectConstantValue value = metadata.constant.value; |
- ClassElement cls = value.type.element; |
- if (cls == noInlineClass) { |
- hasNoInline = true; |
- if (VERBOSE_OPTIMIZER_HINTS) { |
- compiler.reportHint(element, |
- MessageKind.GENERIC, |
- {'text': "Cannot inline"}); |
- } |
- inlineCache.markAsNonInlinable(element); |
- } else if (cls == noThrowsClass) { |
- hasNoThrows = true; |
- if (!Elements.isStaticOrTopLevelFunction(element)) { |
- compiler.internalError(element, |
- "@NoThrows() is currently limited to top-level" |
- " or static functions"); |
- } |
- if (VERBOSE_OPTIMIZER_HINTS) { |
- compiler.reportHint(element, |
- MessageKind.GENERIC, |
- {'text': "Cannot throw"}); |
- } |
- compiler.world.registerCannotThrow(element); |
- } else if (cls == noSideEffectsClass) { |
- hasNoSideEffects = true; |
- if (VERBOSE_OPTIMIZER_HINTS) { |
- compiler.reportHint(element, |
- MessageKind.GENERIC, |
- {'text': "Has no side effects"}); |
- } |
- compiler.world.registerSideEffectsFree(element); |
- } |
- } |
- if (hasNoThrows && !hasNoInline) { |
- compiler.internalError(element, |
- "@NoThrows() should always be combined with @NoInline."); |
- } |
- if (hasNoSideEffects && !hasNoInline) { |
- compiler.internalError(element, |
- "@NoSideEffects() should always be combined with @NoInline."); |
- } |
- if (element == invokeOnMethod) { |
- compiler.enabledInvokeOn = true; |
- } |
- } |
- |
- CodeBuffer codeOf(Element element) { |
- return generatedCode.containsKey(element) |
- ? jsAst.prettyPrint(generatedCode[element], compiler) |
- : null; |
- } |
- |
- FunctionElement helperForBadMain() => findHelper('badMain'); |
- |
- FunctionElement helperForMissingMain() => findHelper('missingMain'); |
- |
- FunctionElement helperForMainArity() { |
- return findHelper('mainHasTooManyParameters'); |
- } |
- |
- void forgetElement(Element element) { |
- constants.forgetElement(element); |
- constantCompilerTask.dartConstantCompiler.forgetElement(element); |
- } |
- |
- void registerMainHasArguments(Enqueuer enqueuer) { |
- // If the main method takes arguments, this compilation could be the target |
- // of Isolate.spawnUri. Strictly speaking, that can happen also if main |
- // takes no arguments, but in this case the spawned isolate can't |
- // communicate with the spawning isolate. |
- enqueuer.enableIsolateSupport(); |
- } |
-} |
- |
-class JavaScriptResolutionCallbacks extends ResolutionCallbacks { |
- final JavaScriptBackend backend; |
- |
- JavaScriptResolutionCallbacks(this.backend); |
- |
- void registerBackendStaticInvocation(Element element, Registry registry) { |
- registry.registerStaticInvocation(backend.registerBackendUse(element)); |
- } |
- |
- void registerBackendInstantiation(ClassElement element, Registry registry) { |
- backend.registerBackendUse(element); |
- element.ensureResolved(backend.compiler); |
- registry.registerInstantiation(element.rawType); |
- } |
- |
- void onAssert(Send node, Registry registry) { |
- registerBackendStaticInvocation(backend.assertMethod, registry); |
- } |
- |
- void onStringInterpolation(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation( |
- backend.getStringInterpolationHelper(), registry); |
- } |
- |
- void onCatchStatement(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation(backend.getExceptionUnwrapper(), registry); |
- registerBackendInstantiation( |
- backend.jsPlainJavaScriptObjectClass, registry); |
- registerBackendInstantiation( |
- backend.jsUnknownJavaScriptObjectClass, registry); |
- } |
- |
- void onThrowExpression(Registry registry) { |
- assert(registry.isForResolution); |
- // We don't know ahead of time whether we will need the throw in a |
- // statement context or an expression context, so we register both |
- // here, even though we may not need the throwExpression helper. |
- registerBackendStaticInvocation(backend.getWrapExceptionHelper(), registry); |
- registerBackendStaticInvocation( |
- backend.getThrowExpressionHelper(), registry); |
- } |
- |
- void onLazyField(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation(backend.getCyclicThrowHelper(), registry); |
- } |
- |
- void onTypeLiteral(DartType type, Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendInstantiation(backend.typeImplementation, registry); |
- registerBackendStaticInvocation(backend.getCreateRuntimeType(), registry); |
- // TODO(ahe): Might want to register [element] as an instantiated class |
- // when reflection is used. However, as long as we disable tree-shaking |
- // eagerly it doesn't matter. |
- if (type.isTypedef) { |
- backend.compiler.world.allTypedefs.add(type.element); |
- } |
- backend.customElementsAnalysis.registerTypeLiteral(type, registry); |
- } |
- |
- void onStackTraceInCatch(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation(backend.getTraceFromException(), registry); |
- } |
- |
- |
- void onTypeVariableExpression(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation(backend.getSetRuntimeTypeInfo(), registry); |
- registerBackendStaticInvocation(backend.getGetRuntimeTypeInfo(), registry); |
- backend.registerGetRuntimeTypeArgument(registry); |
- registerBackendInstantiation(backend.compiler.listClass, registry); |
- registerBackendStaticInvocation(backend.getRuntimeTypeToString(), registry); |
- registerBackendStaticInvocation(backend.getCreateRuntimeType(), registry); |
- } |
- |
- // TODO(johnniwinther): Maybe split this into [onAssertType] and [onTestType]. |
- void onIsCheck(DartType type, Registry registry) { |
- assert(registry.isForResolution); |
- type = type.unalias(backend.compiler); |
- registerBackendInstantiation(backend.compiler.boolClass, registry); |
- bool inCheckedMode = backend.compiler.enableTypeAssertions; |
- if (inCheckedMode) { |
- registerBackendStaticInvocation(backend.getThrowRuntimeError(), registry); |
- } |
- if (type.isMalformed) { |
- registerBackendStaticInvocation(backend.getThrowTypeError(), registry); |
- } |
- if (!type.treatAsRaw || type.containsTypeVariables) { |
- // TODO(johnniwinther): Investigate why this is needed. |
- registerBackendStaticInvocation( |
- backend.getSetRuntimeTypeInfo(), registry); |
- registerBackendStaticInvocation( |
- backend.getGetRuntimeTypeInfo(), registry); |
- backend.registerGetRuntimeTypeArgument(registry); |
- if (inCheckedMode) { |
- registerBackendStaticInvocation(backend.getAssertSubtype(), registry); |
- } |
- registerBackendStaticInvocation(backend.getCheckSubtype(), registry); |
- if (type.isTypeVariable) { |
- registerBackendStaticInvocation( |
- backend.getCheckSubtypeOfRuntimeType(), registry); |
- if (inCheckedMode) { |
- registerBackendStaticInvocation( |
- backend.getAssertSubtypeOfRuntimeType(), registry); |
- } |
- } |
- registerBackendInstantiation(backend.compiler.listClass, registry); |
- } |
- if (type is FunctionType) { |
- registerBackendStaticInvocation( |
- backend.find(backend.jsHelperLibrary, 'functionTypeTestMetaHelper'), |
- registry); |
- } |
- if (type.element != null && type.element.isNative) { |
- // We will neeed to add the "$is" and "$as" properties on the |
- // JavaScript object prototype, so we make sure |
- // [:defineProperty:] is compiled. |
- registerBackendStaticInvocation( |
- backend.find(backend.jsHelperLibrary, 'defineProperty'), registry); |
- } |
- } |
- |
- void onTypeVariableBoundCheck(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation(backend.getThrowTypeError(), registry); |
- registerBackendStaticInvocation(backend.getAssertIsSubtype(), registry); |
- } |
- |
- void onAbstractClassInstantiation(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation( |
- backend.getThrowAbstractClassInstantiationError(), registry); |
- // Also register the types of the arguments passed to this method. |
- registerBackendInstantiation(backend.compiler.stringClass, registry); |
- } |
- |
- void onFallThroughError(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation(backend.getFallThroughError(), registry); |
- } |
- |
- void onAsCheck(DartType type, Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation(backend.getThrowRuntimeError(), registry); |
- } |
- |
- void onThrowNoSuchMethod(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation(backend.getThrowNoSuchMethod(), registry); |
- // Also register the types of the arguments passed to this method. |
- registerBackendInstantiation(backend.compiler.listClass, registry); |
- registerBackendInstantiation(backend.compiler.stringClass, registry); |
- } |
- |
- void onThrowRuntimeError(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation(backend.getThrowRuntimeError(), registry); |
- // Also register the types of the arguments passed to this method. |
- registerBackendInstantiation(backend.compiler.stringClass, registry); |
- } |
- |
- void onSuperNoSuchMethod(Registry registry) { |
- assert(registry.isForResolution); |
- registerBackendStaticInvocation( |
- backend.getCreateInvocationMirror(), registry); |
- registerBackendStaticInvocation( |
- backend.compiler.objectClass.lookupLocalMember(Compiler.NO_SUCH_METHOD), |
- registry); |
- registerBackendInstantiation(backend.compiler.listClass, registry); |
- } |
- |
- void onConstantMap(Registry registry) { |
- assert(registry.isForResolution); |
- void enqueue(String name) { |
- Element e = backend.find(backend.jsHelperLibrary, name); |
- registerBackendInstantiation(e, registry); |
- } |
- |
- enqueue(JavaScriptMapConstant.DART_CLASS); |
- enqueue(JavaScriptMapConstant.DART_PROTO_CLASS); |
- enqueue(JavaScriptMapConstant.DART_STRING_CLASS); |
- enqueue(JavaScriptMapConstant.DART_GENERAL_CLASS); |
- } |
- |
- /// Called when resolving the `Symbol` constructor. |
- void onSymbolConstructor(Registry registry) { |
- assert(registry.isForResolution); |
- // Make sure that _internals.Symbol.validated is registered. |
- assert(backend.compiler.symbolValidatedConstructor != null); |
- registerBackendStaticInvocation( |
- backend.compiler.symbolValidatedConstructor, registry); |
- } |
-} |
- |
-/// Records that [constant] is used by the element behind [registry]. |
-class Dependency { |
- final ConstantValue constant; |
- final Element annotatedElement; |
- |
- const Dependency(this.constant, this.annotatedElement); |
-} |