Index: pkg/compiler/lib/src/js_backend/native_emitter.dart |
diff --git a/pkg/compiler/lib/src/js_backend/native_emitter.dart b/pkg/compiler/lib/src/js_backend/native_emitter.dart |
deleted file mode 100644 |
index d58559459fe383612e018ced02e7d83443bd215e..0000000000000000000000000000000000000000 |
--- a/pkg/compiler/lib/src/js_backend/native_emitter.dart |
+++ /dev/null |
@@ -1,480 +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; |
- |
-class NativeEmitter { |
- |
- final Map<Element, ClassBuilder> cachedBuilders; |
- |
- final CodeEmitterTask emitterTask; |
- CodeBuffer nativeBuffer; |
- |
- // Native classes found in the application. |
- Set<ClassElement> nativeClasses = new Set<ClassElement>(); |
- |
- // Caches the native subtypes of a native class. |
- Map<ClassElement, List<ClassElement>> subtypes; |
- |
- // Caches the direct native subtypes of a native class. |
- Map<ClassElement, List<ClassElement>> directSubtypes; |
- |
- // Caches the methods that have a native body. |
- Set<FunctionElement> nativeMethods; |
- |
- // Do we need the native emitter to take care of handling |
- // noSuchMethod for us? This flag is set to true in the emitter if |
- // it finds any native class that needs noSuchMethod handling. |
- bool handleNoSuchMethod = false; |
- |
- NativeEmitter(CodeEmitterTask emitterTask) |
- : this.emitterTask = emitterTask, |
- subtypes = new Map<ClassElement, List<ClassElement>>(), |
- directSubtypes = new Map<ClassElement, List<ClassElement>>(), |
- nativeMethods = new Set<FunctionElement>(), |
- nativeBuffer = new CodeBuffer(), |
- cachedBuilders = emitterTask.compiler.cacheStrategy.newMap(); |
- |
- Compiler get compiler => emitterTask.compiler; |
- JavaScriptBackend get backend => compiler.backend; |
- |
- jsAst.Expression get defPropFunction { |
- Element element = backend.findHelper('defineProperty'); |
- return backend.namer.elementAccess(element); |
- } |
- |
- /** |
- * Writes the class definitions for the interceptors to [mainBuffer]. |
- * Writes code to associate dispatch tags with interceptors to [nativeBuffer]. |
- * |
- * The interceptors are filtered to avoid emitting trivial interceptors. For |
- * example, if the program contains no code that can distinguish between the |
- * numerous subclasses of `Element` then we can pretend that `Element` is a |
- * leaf class, and all instances of subclasses of `Element` are instances of |
- * `Element`. |
- * |
- * There is also a performance benefit (in addition to the obvious code size |
- * benefit), due to how [getNativeInterceptor] works. Finding the interceptor |
- * of a leaf class in the hierarchy is more efficient that a non-leaf, so it |
- * improves performance when more classes can be treated as leaves. |
- * |
- * [classes] contains native classes, mixin applications, and user subclasses |
- * of native classes. ONLY the native classes are generated here. [classes] |
- * is sorted in desired output order. |
- * |
- * [additionalProperties] is used to collect properties that are pushed up |
- * from the above optimizations onto a non-native class, e.g, `Interceptor`. |
- */ |
- void generateNativeClasses( |
- List<ClassElement> classes, |
- CodeBuffer mainBuffer, |
- Map<ClassElement, Map<String, jsAst.Expression>> additionalProperties) { |
- // Compute a pre-order traversal of the subclass forest. We actually want a |
- // post-order traversal but it is easier to compute the pre-order and use it |
- // in reverse. |
- |
- List<ClassElement> preOrder = <ClassElement>[]; |
- Set<ClassElement> seen = new Set<ClassElement>(); |
- seen..add(compiler.objectClass) |
- ..add(backend.jsInterceptorClass); |
- void walk(ClassElement element) { |
- if (seen.contains(element)) return; |
- seen.add(element); |
- walk(element.superclass); |
- preOrder.add(element); |
- } |
- classes.forEach(walk); |
- |
- // Generate code for each native class into [ClassBuilder]s. |
- |
- Map<ClassElement, ClassBuilder> builders = |
- new Map<ClassElement, ClassBuilder>(); |
- for (ClassElement classElement in classes) { |
- if (classElement.isNative) { |
- ClassBuilder builder = generateNativeClass(classElement); |
- builders[classElement] = builder; |
- } |
- } |
- |
- // Find which classes are needed and which are non-leaf classes. Any class |
- // that is not needed can be treated as a leaf class equivalent to some |
- // needed class. |
- |
- Set<ClassElement> neededClasses = new Set<ClassElement>(); |
- Set<ClassElement> nonleafClasses = new Set<ClassElement>(); |
- |
- Map<ClassElement, List<ClassElement>> extensionPoints = |
- computeExtensionPoints(preOrder); |
- |
- neededClasses.add(compiler.objectClass); |
- |
- Set<ClassElement> neededByConstant = |
- emitterTask.interceptorsReferencedFromConstants(); |
- Set<ClassElement> modifiedClasses = |
- emitterTask.typeTestEmitter.classesModifiedByEmitRuntimeTypeSupport(); |
- |
- for (ClassElement classElement in preOrder.reversed) { |
- // Post-order traversal ensures we visit the subclasses before their |
- // superclass. This makes it easy to tell if a class is needed because a |
- // subclass is needed. |
- ClassBuilder builder = builders[classElement]; |
- bool needed = false; |
- if (builder == null) { |
- // Mixin applications (native+mixin) are non-native, so [classElement] |
- // has already been emitted as a regular class. Mark [classElement] as |
- // 'needed' to ensure the native superclass is needed. |
- needed = true; |
- } else if (!builder.isTrivial) { |
- needed = true; |
- } else if (neededByConstant.contains(classElement)) { |
- needed = true; |
- } else if (modifiedClasses.contains(classElement)) { |
- // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer |
- // adds information to a class prototype or constructor. |
- needed = true; |
- } else if (extensionPoints.containsKey(classElement)) { |
- needed = true; |
- } |
- if (classElement.isNative && |
- native.nativeTagsForcedNonLeaf(classElement)) { |
- needed = true; |
- nonleafClasses.add(classElement); |
- } |
- |
- if (needed || neededClasses.contains(classElement)) { |
- neededClasses.add(classElement); |
- neededClasses.add(classElement.superclass); |
- nonleafClasses.add(classElement.superclass); |
- } |
- } |
- |
- // Collect all the tags that map to each native class. |
- |
- Map<ClassElement, Set<String>> leafTags = |
- new Map<ClassElement, Set<String>>(); |
- Map<ClassElement, Set<String>> nonleafTags = |
- new Map<ClassElement, Set<String>>(); |
- |
- for (ClassElement classElement in classes) { |
- if (!classElement.isNative) continue; |
- List<String> nativeTags = native.nativeTagsOfClass(classElement); |
- |
- if (nonleafClasses.contains(classElement) || |
- extensionPoints.containsKey(classElement)) { |
- nonleafTags |
- .putIfAbsent(classElement, () => new Set<String>()) |
- .addAll(nativeTags); |
- } else { |
- ClassElement sufficingInterceptor = classElement; |
- while (!neededClasses.contains(sufficingInterceptor)) { |
- sufficingInterceptor = sufficingInterceptor.superclass; |
- } |
- if (sufficingInterceptor == compiler.objectClass) { |
- sufficingInterceptor = backend.jsInterceptorClass; |
- } |
- leafTags |
- .putIfAbsent(sufficingInterceptor, () => new Set<String>()) |
- .addAll(nativeTags); |
- } |
- } |
- |
- // Add properties containing the information needed to construct maps used |
- // by getNativeInterceptor and custom elements. |
- if (compiler.enqueuer.codegen.nativeEnqueuer |
- .hasInstantiatedNativeClasses()) { |
- void generateClassInfo(ClassElement classElement) { |
- // Property has the form: |
- // |
- // "%": "leafTag1|leafTag2|...;nonleafTag1|...;Class1|Class2|...", |
- // |
- // If there is no data following a semicolon, the semicolon can be |
- // omitted. |
- |
- String formatTags(Iterable<String> tags) { |
- if (tags == null) return ''; |
- return (tags.toList()..sort()).join('|'); |
- } |
- |
- List<ClassElement> extensions = extensionPoints[classElement]; |
- |
- String leafStr = formatTags(leafTags[classElement]); |
- String nonleafStr = formatTags(nonleafTags[classElement]); |
- |
- StringBuffer sb = new StringBuffer(leafStr); |
- if (nonleafStr != '') { |
- sb..write(';')..write(nonleafStr); |
- } |
- if (extensions != null) { |
- sb..write(';') |
- ..writeAll(extensions.map(backend.namer.getNameOfClass), '|'); |
- } |
- String encoding = sb.toString(); |
- |
- ClassBuilder builder = builders[classElement]; |
- if (builder == null) { |
- // No builder because this is an intermediate mixin application or |
- // Interceptor - these are not direct native classes. |
- if (encoding != '') { |
- Map<String, jsAst.Expression> properties = |
- additionalProperties.putIfAbsent(classElement, |
- () => new LinkedHashMap<String, jsAst.Expression>()); |
- properties[backend.namer.nativeSpecProperty] = js.string(encoding); |
- } |
- } else { |
- builder.addProperty( |
- backend.namer.nativeSpecProperty, js.string(encoding)); |
- } |
- } |
- generateClassInfo(backend.jsInterceptorClass); |
- for (ClassElement classElement in classes) { |
- generateClassInfo(classElement); |
- } |
- } |
- |
- // Emit the native class interceptors that were actually used. |
- for (ClassElement classElement in classes) { |
- if (!classElement.isNative) continue; |
- if (neededClasses.contains(classElement)) { |
- // Define interceptor class for [classElement]. |
- emitterTask.oldEmitter.classEmitter.emitClassBuilderWithReflectionData( |
- backend.namer.getNameOfClass(classElement), |
- classElement, builders[classElement], |
- emitterTask.oldEmitter.getElementDescriptor(classElement)); |
- emitterTask.oldEmitter.needsDefineClass = true; |
- } |
- } |
- } |
- |
- /** |
- * Computes the native classes that are extended (subclassed) by non-native |
- * classes and the set non-mative classes that extend them. (A List is used |
- * instead of a Set for out stability). |
- */ |
- Map<ClassElement, List<ClassElement>> computeExtensionPoints( |
- List<ClassElement> classes) { |
- ClassElement nativeSuperclassOf(ClassElement element) { |
- if (element == null) return null; |
- if (element.isNative) return element; |
- return nativeSuperclassOf(element.superclass); |
- } |
- |
- ClassElement nativeAncestorOf(ClassElement element) { |
- return nativeSuperclassOf(element.superclass); |
- } |
- |
- Map<ClassElement, List<ClassElement>> map = |
- new Map<ClassElement, List<ClassElement>>(); |
- |
- for (ClassElement classElement in classes) { |
- if (classElement.isNative) continue; |
- ClassElement nativeAncestor = nativeAncestorOf(classElement); |
- if (nativeAncestor != null) { |
- map |
- .putIfAbsent(nativeAncestor, () => <ClassElement>[]) |
- .add(classElement); |
- } |
- } |
- return map; |
- } |
- |
- ClassBuilder generateNativeClass(ClassElement classElement) { |
- ClassBuilder builder; |
- if (compiler.hasIncrementalSupport) { |
- builder = cachedBuilders[classElement]; |
- if (builder != null) return builder; |
- builder = new ClassBuilder(classElement, backend.namer); |
- cachedBuilders[classElement] = builder; |
- } else { |
- builder = new ClassBuilder(classElement, backend.namer); |
- } |
- |
- // TODO(sra): Issue #13731- this is commented out as part of custom element |
- // constructor work. |
- //assert(!classElement.hasBackendMembers); |
- nativeClasses.add(classElement); |
- |
- ClassElement superclass = classElement.superclass; |
- assert(superclass != null); |
- // Fix superclass. TODO(sra): make native classes inherit from Interceptor. |
- assert(superclass != compiler.objectClass); |
- if (superclass == compiler.objectClass) { |
- superclass = backend.jsInterceptorClass; |
- } |
- |
- String superName = backend.namer.getNameOfClass(superclass); |
- |
- emitterTask.oldEmitter.classEmitter.emitClassConstructor( |
- classElement, builder); |
- bool hasFields = emitterTask.oldEmitter.classEmitter.emitFields( |
- classElement, builder, superName, classIsNative: true); |
- int propertyCount = builder.properties.length; |
- emitterTask.oldEmitter.classEmitter.emitClassGettersSetters( |
- classElement, builder); |
- emitterTask.oldEmitter.classEmitter.emitInstanceMembers( |
- classElement, builder); |
- emitterTask.typeTestEmitter.emitIsTests(classElement, builder); |
- |
- if (!hasFields && |
- builder.properties.length == propertyCount && |
- superclass is! MixinApplicationElement) { |
- builder.isTrivial = true; |
- } |
- |
- return builder; |
- } |
- |
- void finishGenerateNativeClasses() { |
- // TODO(sra): Put specialized version of getNativeMethods on |
- // `Object.prototype` to avoid checking in `getInterceptor` and |
- // specializations. |
- } |
- |
- void potentiallyConvertDartClosuresToJs( |
- List<jsAst.Statement> statements, |
- FunctionElement member, |
- List<jsAst.Parameter> stubParameters) { |
- FunctionSignature parameters = member.functionSignature; |
- Element converter = backend.findHelper('convertDartClosureToJS'); |
- jsAst.Expression closureConverter = backend.namer.elementAccess(converter); |
- parameters.forEachParameter((ParameterElement parameter) { |
- String name = parameter.name; |
- // If [name] is not in [stubParameters], then the parameter is an optional |
- // parameter that was not provided for this stub. |
- for (jsAst.Parameter stubParameter in stubParameters) { |
- if (stubParameter.name == name) { |
- DartType type = parameter.type.unalias(compiler); |
- if (type is FunctionType) { |
- // The parameter type is a function type either directly or through |
- // typedef(s). |
- FunctionType functionType = type; |
- int arity = functionType.computeArity(); |
- statements.add( |
- js.statement('# = #(#, $arity)', |
- [name, closureConverter, name])); |
- break; |
- } |
- } |
- } |
- }); |
- } |
- |
- List<jsAst.Statement> generateParameterStubStatements( |
- FunctionElement member, |
- bool isInterceptedMethod, |
- String invocationName, |
- List<jsAst.Parameter> stubParameters, |
- List<jsAst.Expression> argumentsBuffer, |
- int indexOfLastOptionalArgumentInParameters) { |
- // The target JS function may check arguments.length so we need to |
- // make sure not to pass any unspecified optional arguments to it. |
- // For example, for the following Dart method: |
- // foo([x, y, z]); |
- // The call: |
- // foo(y: 1) |
- // must be turned into a JS call to: |
- // foo(null, y). |
- |
- ClassElement classElement = member.enclosingClass; |
- |
- List<jsAst.Statement> statements = <jsAst.Statement>[]; |
- potentiallyConvertDartClosuresToJs(statements, member, stubParameters); |
- |
- String target; |
- jsAst.Expression receiver; |
- List<jsAst.Expression> arguments; |
- |
- assert(invariant(member, nativeMethods.contains(member))); |
- // When calling a JS method, we call it with the native name, and only the |
- // arguments up until the last one provided. |
- target = member.fixedBackendName; |
- |
- if (isInterceptedMethod) { |
- receiver = argumentsBuffer[0]; |
- arguments = argumentsBuffer.sublist(1, |
- indexOfLastOptionalArgumentInParameters + 1); |
- } else { |
- receiver = js('this'); |
- arguments = argumentsBuffer.sublist(0, |
- indexOfLastOptionalArgumentInParameters + 1); |
- } |
- statements.add( |
- js.statement('return #.#(#)', [receiver, target, arguments])); |
- |
- return statements; |
- } |
- |
- bool isSupertypeOfNativeClass(Element element) { |
- if (element.isTypeVariable) { |
- compiler.internalError(element, "Is check for type variable."); |
- return false; |
- } |
- if (element.computeType(compiler).unalias(compiler) is FunctionType) { |
- // The element type is a function type either directly or through |
- // typedef(s). |
- return false; |
- } |
- |
- if (!element.isClass) { |
- compiler.internalError(element, "Is check does not handle element."); |
- return false; |
- } |
- |
- if (backend.classesMixedIntoInterceptedClasses.contains(element)) { |
- return true; |
- } |
- |
- return subtypes[element] != null; |
- } |
- |
- bool requiresNativeIsCheck(Element element) { |
- // TODO(sra): Remove this function. It determines if a native type may |
- // satisfy a check against [element], in which case an interceptor must be |
- // used. We should also use an interceptor if the check can't be satisfied |
- // by a native class in case we get a native instance that tries to spoof |
- // the type info. i.e the criteria for whether or not to use an interceptor |
- // is whether the receiver can be native, not the type of the test. |
- if (element == null || !element.isClass) return false; |
- ClassElement cls = element; |
- if (Elements.isNativeOrExtendsNative(cls)) return true; |
- return isSupertypeOfNativeClass(element); |
- } |
- |
- void assembleCode(CodeBuffer targetBuffer) { |
- List<jsAst.Property> objectProperties = <jsAst.Property>[]; |
- |
- jsAst.Property addProperty(String name, jsAst.Expression value) { |
- jsAst.Property prop = new jsAst.Property(js.string(name), value); |
- objectProperties.add(prop); |
- return prop; |
- } |
- |
- if (!nativeClasses.isEmpty) { |
- // If the native emitter has been asked to take care of the |
- // noSuchMethod handlers, we do that now. |
- if (handleNoSuchMethod) { |
- emitterTask.oldEmitter.nsmEmitter.emitNoSuchMethodHandlers(addProperty); |
- } |
- } |
- |
- // If we have any properties to add to Object.prototype, we run |
- // through them and add them using defineProperty. |
- if (!objectProperties.isEmpty) { |
- jsAst.Expression init = js(r''' |
- (function(table) { |
- for(var key in table) |
- #(Object.prototype, key, table[key]); |
- })(#)''', |
- [ defPropFunction, |
- new jsAst.ObjectInitializer(objectProperties)]); |
- |
- if (emitterTask.compiler.enableMinification) targetBuffer.add(';'); |
- targetBuffer.add(jsAst.prettyPrint( |
- new jsAst.ExpressionStatement(init), compiler)); |
- targetBuffer.add('\n'); |
- } |
- |
- targetBuffer.add(nativeBuffer); |
- targetBuffer.add('\n'); |
- } |
-} |