| 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');
|
| - }
|
| -}
|
|
|