Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(160)

Unified Diff: lib/compiler/implementation/emitter.dart

Issue 10854066: Start moving the JavaScript backend related code into a separate library (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add missing import Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/compiler/implementation/compiler.dart ('k') | lib/compiler/implementation/js_backend/backend.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/compiler/implementation/emitter.dart
diff --git a/lib/compiler/implementation/emitter.dart b/lib/compiler/implementation/emitter.dart
deleted file mode 100644
index 43a43ae399641664fd5889888b53e383bbd88762..0000000000000000000000000000000000000000
--- a/lib/compiler/implementation/emitter.dart
+++ /dev/null
@@ -1,1182 +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.
-
-/**
- * A function element that represents a closure call. The signature is copied
- * from the given element.
- */
-class ClosureInvocationElement extends FunctionElement {
- ClosureInvocationElement(SourceString name,
- FunctionElement other)
- : super.from(name, other, other.enclosingElement);
-
- isInstanceMember() => true;
-}
-
-/**
- * Generates the code for all used classes in the program. Static fields (even
- * in classes) are ignored, since they can be treated as non-class elements.
- *
- * The code for the containing (used) methods must exist in the [:universe:].
- */
-class CodeEmitterTask extends CompilerTask {
- bool needsInheritFunction = false;
- bool needsDefineClass = false;
- bool needsClosureClass = false;
- final Namer namer;
- NativeEmitter nativeEmitter;
- CodeBuffer boundClosureBuffer;
- CodeBuffer mainBuffer;
- /** Shorter access to [isolatePropertiesName]. Both here in the code, as
- well as in the generated code. */
- String isolateProperties;
- String classesCollector;
- final Map<int, String> boundClosureCache;
-
- final bool generateSourceMap;
- final SourceMapBuilder sourceMapBuilder;
-
- CodeEmitterTask(Compiler compiler, [bool generateSourceMap = false])
- : namer = compiler.namer,
- boundClosureBuffer = new CodeBuffer(),
- mainBuffer = new CodeBuffer(),
- boundClosureCache = new Map<int, String>(),
- generateSourceMap = generateSourceMap,
- sourceMapBuilder = new SourceMapBuilder(),
- super(compiler) {
- nativeEmitter = new NativeEmitter(this);
- }
-
- String get name() => 'CodeEmitter';
-
- String get defineClassName()
- => '${namer.ISOLATE}.\$defineClass';
- String get finishClassesName()
- => '${namer.ISOLATE}.\$finishClasses';
- String get finishIsolateConstructorName()
- => '${namer.ISOLATE}.\$finishIsolateConstructor';
- String get pendingClassesName()
- => '${namer.ISOLATE}.\$pendingClasses';
- String get isolatePropertiesName()
- => '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}';
- String get supportsProtoName()
- => 'supportsProto';
-
- final String GETTER_SUFFIX = "?";
- final String SETTER_SUFFIX = "!";
- final String GETTER_SETTER_SUFFIX = "=";
-
- String get generateGetterSetterFunction() {
- return """
-function(field, prototype) {
- var len = field.length;
- var lastChar = field[len - 1];
- var needsGetter = lastChar == '$GETTER_SUFFIX' || lastChar == '$GETTER_SETTER_SUFFIX';
- var needsSetter = lastChar == '$SETTER_SUFFIX' || lastChar == '$GETTER_SETTER_SUFFIX';
- if (needsGetter || needsSetter) field = field.substring(0, len - 1);
- if (needsGetter) {
- var getterString = "return this." + field + ";";
-""" /* The supportsProtoCheck below depends on the getter/setter convention.
- When changing here, update the protoCheck too. */ """
- prototype["get\$" + field] = new Function(getterString);
- }
- if (needsSetter) {
- var setterString = "this." + field + " = v;";
- prototype["set\$" + field] = new Function("v", setterString);
- }
- return field;
-}""";
- }
-
- String get defineClassFunction() {
- // First the class name, then the super class name, followed by the fields
- // (in an array) and the members (inside an Object literal).
- // The caller can also pass in the constructor as a function if needed.
- //
- // Example:
- // defineClass("A", "B", ["x", "y"], {
- // foo$1: function(y) {
- // print(this.x + y);
- // },
- // bar$2: function(t, v) {
- // this.x = t - v;
- // },
- // });
- return """
-function(cls, fields, prototype) {
- var generateGetterSetter = $generateGetterSetterFunction;
- var constructor;
- if (typeof fields == 'function') {
- constructor = fields;
- } else {
- var str = "function " + cls + "(";
- var body = "";
- for (var i = 0; i < fields.length; i++) {
- if (i != 0) str += ", ";
- var field = fields[i];
- field = generateGetterSetter(field, prototype);
- str += field;
- body += "this." + field + " = " + field + ";\\n";
- }
- str += ") {" + body + "}\\n";
- str += "return " + cls + ";";
- constructor = new Function(str)();
- }
- constructor.prototype = prototype;
- return constructor;
-}""";
- }
-
- /** Needs defineClass to be defined. */
- String get protoSupportCheck() {
- // On Firefox and Webkit browsers we can manipulate the __proto__
- // directly. Opera claims to have __proto__ support, but it is buggy.
- // So we have to do more checks.
- // If the browser does not support __proto__ we need to instantiate an
- // object with the correct (internal) prototype set up correctly, and then
- // copy the members.
-
- return '''
-var $supportsProtoName = false;
-var tmp = $defineClassName('c', ['f?'], {}).prototype;
-if (tmp.__proto__) {
- tmp.__proto__ = {};
- if (typeof tmp.get\$f !== "undefined") $supportsProtoName = true;
-}
-''';
- }
-
- String get finishClassesFunction() {
- // 'defineClass' does not require the classes to be constructed in order.
- // Classes are initially just stored in the 'pendingClasses' field.
- // 'finishClasses' takes all pending classes and sets up the prototype.
- // Once set up, the constructors prototype field satisfy:
- // - it contains all (local) members.
- // - its internal prototype (__proto__) points to the superclass'
- // prototype field.
- // - the prototype's constructor field points to the JavaScript
- // constructor.
- // For engines where we have access to the '__proto__' we can manipulate
- // the object literal directly. For other engines we have to create a new
- // object and copy over the members.
- return '''
-function(collectedClasses) {
- for (var cls in collectedClasses) {
- if (Object.prototype.hasOwnProperty.call(collectedClasses, cls)) {
- var desc = collectedClasses[cls];
- $isolatePropertiesName[cls] = $defineClassName(cls, desc[''], desc);
- if (desc['super'] !== "") $pendingClassesName[cls] = desc['super'];
- }
- }
- var pendingClasses = $pendingClassesName;
-'''/* FinishClasses can be called multiple times. This means that we need to
- clear the pendingClasses property. */'''
- $pendingClassesName = {};
- var finishedClasses = {};
- function finishClass(cls) {
- if (finishedClasses[cls]) return;
- finishedClasses[cls] = true;
- var superclass = pendingClasses[cls];
-'''/* The superclass is only false (empty string) for Dart's Object class. */'''
- if (!superclass) return;
- finishClass(superclass);
- var constructor = $isolatePropertiesName[cls];
- var superConstructor = $isolatePropertiesName[superclass];
- var prototype = constructor.prototype;
- if ($supportsProtoName) {
- prototype.__proto__ = superConstructor.prototype;
- prototype.constructor = constructor;
- } else {
- function tmp() {};
- tmp.prototype = superConstructor.prototype;
- var newPrototype = new tmp();
- constructor.prototype = newPrototype;
- newPrototype.constructor = constructor;
-'''/* Opera does not support 'getOwnPropertyNames'. Therefore we use
- hosOwnProperty instead. */'''
- var hasOwnProperty = Object.prototype.hasOwnProperty;
- for (var member in prototype) {
- if (member == '' || member == 'super') continue;
- if (hasOwnProperty.call(prototype, member)) {
- newPrototype[member] = prototype[member];
- }
- }
- }
- }
- for (var cls in pendingClasses) finishClass(cls);
-}''';
- }
-
- String get finishIsolateConstructorFunction() {
- String isolate = namer.ISOLATE;
- // We replace the old Isolate function with a new one that initializes
- // all its field with the initial (and often final) value of all globals.
- // This has two advantages:
- // 1. the properties are in the object itself (thus avoiding to go through
- // the prototype when looking up globals.
- // 2. a new isolate goes through a (usually well optimized) constructor
- // function of the form: "function() { this.x = ...; this.y = ...; }".
- //
- // Example: If [isolateProperties] is an object containing: x = 3 and
- // A = function A() { /* constructor of class A. */ }, then we generate:
- // str = "{
- // var isolateProperties = Isolate.$isolateProperties;
- // this.x = isolateProperties.x;
- // this.A = isolateProperties.A;
- // }";
- // which is then dynamically evaluated:
- // var newIsolate = new Function(str);
- //
- // We also copy over old values like the prototype, and the
- // isolateProperties themselves.
- return """function(oldIsolate) {
- var isolateProperties = oldIsolate.${namer.ISOLATE_PROPERTIES};
- var isolatePrototype = oldIsolate.prototype;
- var str = "{\\n";
- str += "var properties = $isolate.${namer.ISOLATE_PROPERTIES};\\n";
- for (var staticName in isolateProperties) {
- if (Object.prototype.hasOwnProperty.call(isolateProperties, staticName)) {
- str += "this." + staticName + "= properties." + staticName + ";\\n";
- }
- }
- str += "}\\n";
- var newIsolate = new Function(str);
- newIsolate.prototype = isolatePrototype;
- isolatePrototype.constructor = newIsolate;
- newIsolate.${namer.ISOLATE_PROPERTIES} = isolateProperties;
- return newIsolate;
-}""";
- }
-
- void addDefineClassAndFinishClassFunctionsIfNecessary(CodeBuffer buffer) {
- if (needsDefineClass) {
- String isolate = namer.ISOLATE;
- buffer.add("$defineClassName = $defineClassFunction;\n");
- buffer.add(protoSupportCheck);
- buffer.add("$pendingClassesName = {};\n");
- buffer.add("$finishClassesName = $finishClassesFunction;\n");
- }
- }
-
- void emitFinishIsolateConstructor(CodeBuffer buffer) {
- String name = finishIsolateConstructorName;
- String value = finishIsolateConstructorFunction;
- buffer.add("$name = $value;\n");
- }
-
- void emitFinishIsolateConstructorInvocation(CodeBuffer buffer) {
- String isolate = namer.ISOLATE;
- buffer.add("$isolate = $finishIsolateConstructorName($isolate);\n");
- }
-
- void addParameterStub(FunctionElement member,
- Selector selector,
- DefineMemberFunction defineInstanceMember) {
- FunctionSignature parameters = member.computeSignature(compiler);
- int positionalArgumentCount = selector.positionalArgumentCount;
- if (positionalArgumentCount == parameters.parameterCount) {
- assert(selector.namedArgumentCount == 0);
- return;
- }
- ConstantHandler handler = compiler.constantHandler;
- List<SourceString> names = selector.getOrderedNamedArguments();
-
- String invocationName =
- namer.instanceMethodInvocationName(member.getLibrary(), member.name,
- selector);
- CodeBuffer buffer = new CodeBuffer();
- buffer.add('function(');
-
- // The parameters that this stub takes.
- List<String> parametersBuffer = new List<String>(selector.argumentCount);
- // The arguments that will be passed to the real method.
- List<String> argumentsBuffer = new List<String>(parameters.parameterCount);
-
- // We fill the lists depending on the selector. For example,
- // take method foo:
- // foo(a, b, [c, d]);
- //
- // We may have multiple ways of calling foo:
- // (1) foo(1, 2, 3, 4)
- // (2) foo(1, 2);
- // (3) foo(1, 2, 3);
- // (4) foo(1, 2, c: 3);
- // (5) foo(1, 2, d: 4);
- // (6) foo(1, 2, c: 3, d: 4);
- // (7) foo(1, 2, d: 4, c: 3);
- //
- // What we generate at the call sites are:
- // (1) foo$4(1, 2, 3, 4)
- // (2) foo$2(1, 2);
- // (3) foo$3(1, 2, 3);
- // (4) foo$3$c(1, 2, 3);
- // (5) foo$3$d(1, 2, 4);
- // (6) foo$4$c$d(1, 2, 3, 4);
- // (7) foo$4$c$d(1, 2, 3, 4);
- //
- // The stubs we generate are (expressed in Dart):
- // (1) No stub generated, call is direct.
- // (2) foo$2(a, b) => foo$4(a, b, null, null)
- // (3) foo$3(a, b, c) => foo$4(a, b, c, null)
- // (4) foo$3$c(a, b, c) => foo$4(a, b, c, null);
- // (5) foo$3$d(a, b, d) => foo$4(a, b, null, d);
- // (6) foo$4$c$d(a, b, c, d) => foo$4(a, b, c, d);
- // (7) Same as (5).
- //
- // We need to generate a stub for (5) because the order of the
- // stub arguments and the real method may be different.
-
- int count = 0;
- int indexOfLastOptionalArgumentInParameters = positionalArgumentCount - 1;
- parameters.forEachParameter((Element element) {
- String jsName = JsNames.getValid(element.name.slowToString());
- if (count < positionalArgumentCount) {
- parametersBuffer[count] = jsName;
- argumentsBuffer[count] = jsName;
- } else {
- int index = names.indexOf(element.name);
- if (index != -1) {
- indexOfLastOptionalArgumentInParameters = count;
- // The order of the named arguments is not the same as the
- // one in the real method (which is in Dart source order).
- argumentsBuffer[count] = jsName;
- parametersBuffer[selector.positionalArgumentCount + index] = jsName;
- } else {
- Constant value = handler.initialVariableValues[element];
- if (value == null) {
- argumentsBuffer[count] = NullConstant.JsNull;
- } else {
- if (!value.isNull()) {
- // If the value is the null constant, we should not pass it
- // down to the native method.
- indexOfLastOptionalArgumentInParameters = count;
- }
- CodeBuffer argumentBuffer = new CodeBuffer();
- handler.writeConstant(argumentBuffer, value);
- argumentsBuffer[count] = argumentBuffer.toString();
- }
- }
- }
- count++;
- });
- String parametersString = Strings.join(parametersBuffer, ",");
- buffer.add('$parametersString) {\n');
-
- if (member.isNative()) {
- nativeEmitter.generateParameterStub(
- member, invocationName, parametersString, argumentsBuffer,
- indexOfLastOptionalArgumentInParameters, buffer);
- } else {
- String arguments = Strings.join(argumentsBuffer, ",");
- buffer.add(' return this.${namer.getName(member)}($arguments)');
- }
- buffer.add('\n}');
- defineInstanceMember(invocationName, buffer);
- }
-
- void addParameterStubs(FunctionElement member,
- DefineMemberFunction defineInstanceMember) {
- Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
- if (selectors == null) return;
- for (Selector selector in selectors) {
- if (!selector.applies(member, compiler)) continue;
- addParameterStub(member, selector, defineInstanceMember);
- }
- }
-
- bool instanceFieldNeedsGetter(Element member) {
- assert(member.kind === ElementKind.FIELD);
- return compiler.codegenWorld.hasInvokedGetter(member, compiler);
- }
-
- bool instanceFieldNeedsSetter(Element member) {
- assert(member.kind === ElementKind.FIELD);
- return (member.modifiers === null || !member.modifiers.isFinal())
- && compiler.codegenWorld.hasInvokedSetter(member, compiler);
- }
-
- String compiledFieldName(Element member) {
- assert(member.kind === ElementKind.FIELD);
- return member.isNative()
- ? member.name.slowToString()
- : namer.getName(member);
- }
-
- void addInstanceMember(Element member,
- DefineMemberFunction defineInstanceMember) {
- // TODO(floitsch): we don't need to deal with members of
- // uninstantiated classes, that have been overwritten by subclasses.
-
- if (member.kind === ElementKind.FUNCTION
- || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY
- || member.kind === ElementKind.GETTER
- || member.kind === ElementKind.SETTER) {
- if (member.modifiers !== null && member.modifiers.isAbstract()) return;
- CodeBuffer codeBuffer = compiler.codegenWorld.generatedCode[member];
- if (codeBuffer == null) return;
- defineInstanceMember(namer.getName(member), codeBuffer);
- codeBuffer = compiler.codegenWorld.generatedBailoutCode[member];
- if (codeBuffer !== null) {
- defineInstanceMember(compiler.namer.getBailoutName(member), codeBuffer);
- }
- FunctionElement function = member;
- FunctionSignature parameters = function.computeSignature(compiler);
- if (!parameters.optionalParameters.isEmpty()) {
- addParameterStubs(member, defineInstanceMember);
- }
- } else if (member.kind !== ElementKind.FIELD) {
- compiler.internalError('unexpected kind: "${member.kind}"',
- element: member);
- }
- emitExtraAccessors(member, defineInstanceMember);
- }
-
- Set<Element> emitClassFields(ClassElement classElement, CodeBuffer buffer) {
- // If the class is never instantiated we still need to set it up for
- // inheritance purposes, but we can simplify its JavaScript constructor.
- bool isInstantiated =
- compiler.codegenWorld.instantiatedClasses.contains(classElement);
-
- bool isFirstField = true;
- void addField(ClassElement enclosingClass, Element member) {
- assert(!member.isNative());
-
- LibraryElement library = member.getLibrary();
- SourceString name = member.name;
- bool isPrivate = name.isPrivate();
- // See if we can dynamically create getters and setters.
- // We can only generate getters and setters for [classElement] since
- // the fields of super classes could be overwritten with getters or
- // setters.
- bool needsDynamicGetter = false;
- bool needsDynamicSetter = false;
- // We need to name shadowed fields differently, so they don't clash with
- // the non-shadowed field.
- bool isShadowed = false;
- if (enclosingClass === classElement) {
- needsDynamicGetter = instanceFieldNeedsGetter(member);
- needsDynamicSetter = instanceFieldNeedsSetter(member);
- } else {
- isShadowed = classElement.isShadowedByField(member);
- }
-
- if ((isInstantiated && !enclosingClass.isNative())
- || needsDynamicGetter
- || needsDynamicSetter) {
- if (isFirstField) {
- isFirstField = false;
- } else {
- buffer.add(", ");
- }
- String fieldName = isShadowed
- ? namer.shadowedFieldName(member)
- : namer.instanceFieldName(library, name);
- // Getters and setters with suffixes will be generated dynamically.
- buffer.add('"$fieldName');
- if (needsDynamicGetter || needsDynamicSetter) {
- if (needsDynamicGetter && needsDynamicSetter) {
- buffer.add(GETTER_SETTER_SUFFIX);
- } else if (needsDynamicGetter) {
- buffer.add(GETTER_SUFFIX);
- } else {
- buffer.add(SETTER_SUFFIX);
- }
- }
- buffer.add('"');
- }
- }
-
- // If a class is not instantiated then we add the field just so we can
- // generate the field getter/setter dynamically. Since this is only
- // allowed on fields that are in [classElement] we don't need to visit
- // superclasses for non-instantiated classes.
- classElement.forEachInstanceField(
- addField,
- includeBackendMembers: true,
- includeSuperMembers: isInstantiated && !classElement.isNative());
- }
-
- void emitInstanceMembers(ClassElement classElement,
- CodeBuffer buffer,
- bool needsLeadingComma) {
- bool needsComma = needsLeadingComma;
- void defineInstanceMember(String name, CodeBuffer memberBuffer) {
- if (needsComma) buffer.add(',');
- needsComma = true;
- buffer.add('\n');
- buffer.add(' $name: ');
- addMappings(memberBuffer, buffer.length);
- buffer.add(memberBuffer);
- }
-
- classElement.forEachMember(includeBackendMembers: true,
- f: (ClassElement enclosing, Element member) {
- if (member.isInstanceMember()) {
- addInstanceMember(member, defineInstanceMember);
- }
- });
-
- generateTypeTests(classElement, (Element other) {
- String code;
- if (nativeEmitter.requiresNativeIsCheck(other)) {
- code = 'function() { return true; }';
- } else {
- code = 'true';
- }
- CodeBuffer typeTestBuffer = new CodeBuffer();
- typeTestBuffer.add(code);
- defineInstanceMember(namer.operatorIs(other), typeTestBuffer);
- });
-
- if (classElement === compiler.objectClass && compiler.enabledNoSuchMethod) {
- // Emit the noSuchMethod handlers on the Object prototype now,
- // so that the code in the dynamicFunction helper can find
- // them. Note that this helper is invoked before analyzing the
- // full JS script.
- if (!nativeEmitter.handleNoSuchMethod) {
- emitNoSuchMethodHandlers(defineInstanceMember);
- }
- }
- }
-
- void generateClass(ClassElement classElement, CodeBuffer buffer) {
- if (classElement.isNative()) {
- nativeEmitter.generateNativeClass(classElement);
- return;
- } else {
- // TODO(ngeoffray): Instead of switching between buffer, we
- // should create code sections, and decide where to emit them at
- // the end.
- buffer = mainBuffer;
- }
-
- needsDefineClass = true;
- String className = namer.getName(classElement);
- ClassElement superclass = classElement.superclass;
- String superName = "";
- if (superclass !== null) {
- superName = namer.getName(superclass);
- }
- String constructorName = namer.safeName(classElement.name.slowToString());
-
- buffer.add('$classesCollector.$className = {"":\n');
- buffer.add(' [');
- emitClassFields(classElement, buffer);
- buffer.add('],\n');
- // TODO(floitsch): the emitInstanceMember should simply always emit a ',\n'.
- // That does currently not work because the native classes have a different
- // syntax.
- buffer.add(' super: "$superName"');
- emitInstanceMembers(classElement, buffer, true);
- buffer.add('\n};\n\n');
- }
-
- void generateTypeTests(ClassElement cls,
- void generateTypeTest(ClassElement element)) {
- if (compiler.codegenWorld.isChecks.contains(cls)) {
- generateTypeTest(cls);
- }
- generateInterfacesIsTests(cls, generateTypeTest, new Set<Element>());
- }
-
- void generateInterfacesIsTests(ClassElement cls,
- void generateTypeTest(ClassElement element),
- Set<Element> alreadyGenerated) {
- for (Type interfaceType in cls.interfaces) {
- Element element = interfaceType.element;
- if (!alreadyGenerated.contains(element) &&
- compiler.codegenWorld.isChecks.contains(element)) {
- alreadyGenerated.add(element);
- generateTypeTest(element);
- }
- generateInterfacesIsTests(element, generateTypeTest, alreadyGenerated);
- }
- }
-
- void emitClasses(CodeBuffer buffer) {
- Set<ClassElement> instantiatedClasses =
- compiler.codegenWorld.instantiatedClasses;
- Set<ClassElement> neededClasses =
- new Set<ClassElement>.from(instantiatedClasses);
- for (ClassElement element in instantiatedClasses) {
- for (ClassElement superclass = element.superclass;
- superclass !== null;
- superclass = superclass.superclass) {
- if (neededClasses.contains(superclass)) break;
- neededClasses.add(superclass);
- }
- }
- List<ClassElement> sortedClasses =
- new List<ClassElement>.from(neededClasses);
- sortedClasses.sort((ClassElement class1, ClassElement class2) {
- // We sort by the ids of the classes. There is no guarantee that these
- // ids are meaningful (or even deterministic), but in the current
- // implementation they are increasing within a source file.
- return class1.id - class2.id;
- });
-
- // If we need noSuchMethod support, we run through all needed
- // classes to figure out if we need the support on any native
- // class. If so, we let the native emitter deal with it.
- if (compiler.enabledNoSuchMethod) {
- SourceString noSuchMethodName = Compiler.NO_SUCH_METHOD;
- for (ClassElement element in sortedClasses) {
- if (!element.isNative()) continue;
- Element member = element.lookupLocalMember(noSuchMethodName);
- if (member === null) continue;
- if (Selector.INVOCATION_2.applies(member, compiler)) {
- nativeEmitter.handleNoSuchMethod = true;
- break;
- }
- }
- }
-
- for (ClassElement element in sortedClasses) {
- generateClass(element, buffer);
- }
-
- // The closure class could have become necessary because of the generation
- // of stubs.
- ClassElement closureClass = compiler.closureClass;
- if (needsClosureClass && !instantiatedClasses.contains(closureClass)) {
- generateClass(closureClass, buffer);
- }
- }
-
- void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) {
- if (needsDefineClass) {
- buffer.add("$finishClassesName($classesCollector);\n");
- // Reset the map.
- buffer.add("$classesCollector = {};\n");
- }
- }
-
- void emitStaticFunctionsWithNamer(CodeBuffer buffer,
- Map<Element, CodeBuffer> generatedCode,
- String functionNamer(Element element)) {
- generatedCode.forEach((Element element, CodeBuffer functionBuffer) {
- if (!element.isInstanceMember()) {
- String functionName = functionNamer(element);
- buffer.add('$isolateProperties.$functionName = ');
- addMappings(functionBuffer, buffer.length);
- buffer.add(functionBuffer);
- buffer.add(';\n\n');
- }
- });
- }
-
- void emitStaticFunctions(CodeBuffer buffer) {
- emitStaticFunctionsWithNamer(buffer,
- compiler.codegenWorld.generatedCode,
- namer.getName);
- emitStaticFunctionsWithNamer(buffer,
- compiler.codegenWorld.generatedBailoutCode,
- namer.getBailoutName);
- }
-
- void emitStaticFunctionGetters(CodeBuffer buffer) {
- Set<FunctionElement> functionsNeedingGetter =
- compiler.codegenWorld.staticFunctionsNeedingGetter;
- for (FunctionElement element in functionsNeedingGetter) {
- // The static function does not have the correct name. Since
- // [addParameterStubs] use the name to create its stubs we simply
- // create a fake element with the correct name.
- // Note: the callElement will not have any enclosingElement.
- FunctionElement callElement =
- new ClosureInvocationElement(namer.CLOSURE_INVOCATION_NAME, element);
- String staticName = namer.getName(element);
- int parameterCount = element.parameterCount(compiler);
- String invocationName =
- namer.instanceMethodName(element.getLibrary(), callElement.name,
- parameterCount);
- String fieldAccess = '$isolateProperties.$staticName';
- buffer.add("$fieldAccess.$invocationName = $fieldAccess;\n");
- addParameterStubs(callElement, (String name, CodeBuffer value) {
- buffer.add('$fieldAccess.$name = $value;\n');
- });
- // If a static function is used as a closure we need to add its name
- // in case it is used in spawnFunction.
- String fieldName = namer.STATIC_CLOSURE_NAME_NAME;
- buffer.add('$fieldAccess.$fieldName = "$staticName";\n');
- }
- }
-
- void emitDynamicFunctionGetter(FunctionElement member,
- DefineMemberFunction defineInstanceMember) {
- // For every method that has the same name as a property-get we create a
- // getter that returns a bound closure. Say we have a class 'A' with method
- // 'foo' and somewhere in the code there is a dynamic property get of
- // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript):
- //
- // class A {
- // foo(x, y, z) { ... } // Original function.
- // get foo() { return new BoundClosure499(this, "foo"); }
- // }
- // class BoundClosure499 extends Closure {
- // var self;
- // BoundClosure499(this.self, this.name);
- // $call3(x, y, z) { return self[name](x, y, z); }
- // }
-
- // TODO(floitsch): share the closure classes with other classes
- // if they share methods with the same signature. Currently we do this only
- // if there are no optional parameters. Closures with optional parameters
- // are more difficult to canonicalize because they would need to have the
- // same default values.
-
- bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0;
- int parameterCount = member.parameterCount(compiler);
-
- String closureClass =
- hasOptionalParameters ? null : boundClosureCache[parameterCount];
- if (closureClass === null) {
- // Either the class was not cached yet, or there are optional parameters.
- // Create a new closure class.
- SourceString name = const SourceString("BoundClosure");
- ClassElement closureClassElement =
- new ClosureClassElement(name, compiler, member.getCompilationUnit());
- String mangledName = namer.getName(closureClassElement);
- String superName = namer.getName(closureClassElement.superclass);
- needsClosureClass = true;
-
- // Define the constructor with a name so that Object.toString can
- // find the class name of the closure class.
- boundClosureBuffer.add("""
-$classesCollector.$mangledName = {'':
- ['self', 'target'],
- 'super': '$superName',
-""");
- // Now add the methods on the closure class. The instance method does not
- // have the correct name. Since [addParameterStubs] use the name to create
- // its stubs we simply create a fake element with the correct name.
- // Note: the callElement will not have any enclosingElement.
- FunctionElement callElement =
- new ClosureInvocationElement(namer.CLOSURE_INVOCATION_NAME, member);
-
- String invocationName =
- namer.instanceMethodName(member.getLibrary(),
- callElement.name, parameterCount);
- List<String> arguments = new List<String>(parameterCount);
- for (int i = 0; i < parameterCount; i++) {
- arguments[i] = "p$i";
- }
- String joinedArgs = Strings.join(arguments, ", ");
- boundClosureBuffer.add(
- "$invocationName: function($joinedArgs) {");
- boundClosureBuffer.add(" return this.self[this.target]($joinedArgs);");
- boundClosureBuffer.add(" }");
- addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) {
- boundClosureBuffer.add(',\n $stubName: $memberValue');
- });
- boundClosureBuffer.add("\n};\n");
-
- closureClass = namer.isolateAccess(closureClassElement);
-
- // Cache it.
- if (!hasOptionalParameters) {
- boundClosureCache[parameterCount] = closureClass;
- }
- }
-
- // And finally the getter.
- String getterName = namer.getterName(member.getLibrary(), member.name);
- String targetName = namer.instanceMethodName(member.getLibrary(),
- member.name, parameterCount);
- CodeBuffer getterBuffer = new CodeBuffer();
- getterBuffer.add(
- "function() { return new $closureClass(this, '$targetName'); }");
- defineInstanceMember(getterName, getterBuffer);
- }
-
- void emitCallStubForGetter(Element member,
- Set<Selector> selectors,
- DefineMemberFunction defineInstanceMember) {
- String getter;
- if (member.kind == ElementKind.GETTER) {
- getter = "this.${namer.getterName(member.getLibrary(), member.name)}()";
- } else {
- String name = namer.instanceFieldName(member.getLibrary(), member.name);
- getter = "this.$name";
- }
- for (Selector selector in selectors) {
- if (selector.applies(member, compiler)) {
- String invocationName =
- namer.instanceMethodInvocationName(member.getLibrary(), member.name,
- selector);
- SourceString callName = namer.CLOSURE_INVOCATION_NAME;
- String closureCallName =
- namer.instanceMethodInvocationName(member.getLibrary(), callName,
- selector);
- List<String> arguments = <String>[];
- for (int i = 0; i < selector.argumentCount; i++) {
- arguments.add("arg$i");
- }
- String joined = Strings.join(arguments, ", ");
- CodeBuffer getterBuffer = new CodeBuffer();
- getterBuffer.add(
- "function($joined) { return $getter.$closureCallName($joined); }");
- defineInstanceMember(invocationName, getterBuffer);
- }
- }
- }
-
- void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) {
- ConstantHandler handler = compiler.constantHandler;
- List<VariableElement> staticNonFinalFields =
- handler.getStaticNonFinalFieldsForEmission();
- for (Element element in staticNonFinalFields) {
- buffer.add('$isolateProperties.${namer.getName(element)} = ');
- compiler.withCurrentElement(element, () {
- handler.writeJsCodeForVariable(buffer, element);
- });
- buffer.add(';\n');
- }
- }
-
- void emitCompileTimeConstants(CodeBuffer buffer) {
- ConstantHandler handler = compiler.constantHandler;
- List<Constant> constants = handler.getConstantsForEmission();
- bool addedMakeConstantList = false;
- for (Constant constant in constants) {
- String name = handler.getNameForConstant(constant);
- // The name is null when the constant is already a JS constant.
- // TODO(floitsch): every constant should be registered, so that we can
- // share the ones that take up too much space (like some strings).
- if (name === null) continue;
- if (!addedMakeConstantList && constant.isList()) {
- addedMakeConstantList = true;
- emitMakeConstantList(buffer);
- }
- buffer.add('$isolateProperties.$name = ');
- handler.writeJsCode(buffer, constant);
- buffer.add(';\n');
- }
- }
-
- void emitMakeConstantList(CodeBuffer buffer) {
- buffer.add(namer.ISOLATE);
- buffer.add(@'''.makeConstantList = function(list) {
- list.immutable$list = true;
- list.fixed$length = true;
- return list;
-};
-''');
- }
-
- void emitExtraAccessors(Element member,
- DefineMemberFunction defineInstanceMember) {
- if (member.kind == ElementKind.GETTER || member.kind == ElementKind.FIELD) {
- Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
- if (selectors !== null && !selectors.isEmpty()) {
- emitCallStubForGetter(member, selectors, defineInstanceMember);
- }
- } else if (member.kind == ElementKind.FUNCTION) {
- if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) {
- emitDynamicFunctionGetter(member, defineInstanceMember);
- }
- }
- }
-
- void emitNoSuchMethodHandlers(DefineMemberFunction defineInstanceMember) {
- // Do not generate no such method handlers if there is no class.
- if (compiler.codegenWorld.instantiatedClasses.isEmpty()) return;
-
- String noSuchMethodName =
- namer.instanceMethodName(null, Compiler.NO_SUCH_METHOD, 2);
-
- // Keep track of the JavaScript names we've already added so we
- // do not introduce duplicates (bad for code size).
- Set<String> addedJsNames = new Set<String>();
-
- // Keep track of the noSuchMethod holders for each possible
- // receiver type.
- Map<ClassElement, Set<ClassElement>> noSuchMethodHolders =
- new Map<ClassElement, Set<ClassElement>>();
- Set<ClassElement> noSuchMethodHoldersFor(Type type) {
- ClassElement element = type.element;
- Set<ClassElement> result = noSuchMethodHolders[element];
- if (result === null) {
- // For now, we check the entire world to see if an object of
- // the given type may have a user-defined noSuchMethod
- // implementation. We could do better by only looking at
- // instantiated (or otherwise needed) classes.
- result = compiler.world.findNoSuchMethodHolders(type);
- noSuchMethodHolders[element] = result;
- }
- return result;
- }
-
- CodeBuffer generateMethod(String methodName, Selector selector) {
- CodeBuffer args = new CodeBuffer();
- for (int i = 0; i < selector.argumentCount; i++) {
- if (i != 0) args.add(', ');
- args.add('\$$i');
- }
- CodeBuffer buffer = new CodeBuffer();
- buffer.add('function($args) {\n');
- buffer.add(' return this.$noSuchMethodName("$methodName", [$args]);\n');
- buffer.add(' }');
- return buffer;
- }
-
- void addNoSuchMethodHandlers(SourceString name, Set<Selector> selectors) {
- // TODO(kasperl): We should really teach private selectors about
- // which libraries they are used from. That way, we wouldn't
- // have to conservatively generate versions for all libraries
- // the name is used from.
- String nameString = name.slowToString();
- Collection<LibraryElement> libraries = name.isPrivate()
- ? namer.usedPrivateNames[nameString]
- : const [ null ];
-
- // Cache the object class and type.
- ClassElement objectClass = compiler.objectClass;
- Type objectType = objectClass.computeType(compiler);
-
- for (Selector selector in selectors) {
- // Introduce a helper function that determines if the given
- // class has a member that matches the current name and
- // selector (grabbed from the scope).
- bool hasMatchingMember(ClassElement holder) {
- Element element = holder.lookupMember(name);
- if (element === null) return false;
-
- // TODO(kasperl): Consider folding this logic into the
- // Selector.applies() method.
- if (element is AbstractFieldElement) {
- AbstractFieldElement field = element;
- if (selector.kind === SelectorKind.GETTER) {
- return field.getter !== null;
- } else if (selector.kind === SelectorKind.SETTER) {
- return field.setter !== null;
- } else {
- return false;
- }
- }
- return selector.applies(element, compiler);
- }
-
- // If the selector is typed, we check to see if that type may
- // have a user-defined noSuchMethod implementation. If not, we
- // skip the selector altogether.
- Type receiverType = objectType;
- ClassElement receiverClass = objectClass;
- if (selector is TypedSelector) {
- receiverType = (selector as TypedSelector).receiverType;
- receiverClass = receiverType.element;
- }
-
- // If the receiver class is guaranteed to have a member that
- // matches what we're looking for, there's no need to
- // introduce a noSuchMethod handler. It will never be called.
- //
- // As an example, consider this class hierarchy:
- //
- // A <-- noSuchMethod
- // / \
- // C B <-- foo
- //
- // If we know we're calling foo on an object of type B we
- // don't have to worry about the noSuchMethod method in A
- // because objects of type B implement foo. On the other hand,
- // if we end up calling foo on something of type C we have to
- // add a handler for it.
- if (hasMatchingMember(receiverClass)) continue;
-
- // If the holders of all user-defined noSuchMethod
- // implementations that might be applicable to the receiver
- // type have a matching member for the current name and
- // selector, we avoid introducing a noSuchMethod handler.
- //
- // As an example, consider this class hierarchy:
- //
- // A <-- foo
- // / \
- // noSuchMethod --> B C <-- bar
- // | |
- // C D <-- noSuchMethod
- //
- // When calling foo on an object of type A, we know that the
- // implementations of noSuchMethod are in the classes B and D
- // that also (indirectly) implement foo, so we do not need a
- // handler for it.
- //
- // If we're calling bar on an object of type D, we don't need
- // the handler either because all objects of type D implement
- // bar through inheritance.
- //
- // If we're calling bar on an object of type A we do need the
- // handler because we may have to call B.noSuchMethod since B
- // does not implement bar.
- Set<ClassElement> holders = noSuchMethodHoldersFor(receiverType);
- if (holders.every(hasMatchingMember)) continue;
-
- for (LibraryElement lib in libraries) {
- String jsName = null;
- String methodName = null;
- if (selector.kind === SelectorKind.GETTER) {
- jsName = namer.getterName(lib, name);
- methodName = 'get:$nameString';
- } else if (selector.kind === SelectorKind.SETTER) {
- jsName = namer.setterName(lib, name);
- methodName = 'set:$nameString';
- } else if (selector.kind === SelectorKind.INVOCATION) {
- jsName = namer.instanceMethodInvocationName(lib, name, selector);
- methodName = nameString;
- } else {
- // We simply ignore selectors that do not need
- // noSuchMethod handlers.
- continue;
- }
- if (!addedJsNames.contains(jsName)) {
- CodeBuffer jsCode = generateMethod(methodName, selector);
- defineInstanceMember(jsName, jsCode);
- addedJsNames.add(jsName);
- }
- }
- }
- }
-
- compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers);
- compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers);
- compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers);
- }
-
- String buildIsolateSetup(CodeBuffer buffer,
- Element appMain,
- Element isolateMain) {
- String mainAccess = "${namer.isolateAccess(appMain)}";
- String currentIsolate = "${namer.CURRENT_ISOLATE}";
- String mainEnsureGetter = '';
- // Since we pass the closurized version of the main method to
- // the isolate method, we must make sure that it exists.
- if (!compiler.codegenWorld.staticFunctionsNeedingGetter.contains(appMain)) {
- String invocationName =
- "${namer.closureInvocationName(Selector.INVOCATION_0)}";
- mainEnsureGetter = "$mainAccess.$invocationName = $mainAccess";
- }
-
- // TODO(ngeoffray): These globals are currently required by the isolate
- // library, but since leg already generates code on an Isolate object, they
- // are not really needed. We should remove them once Leg replaces Frog.
- buffer.add("""
-var \$globalThis = $currentIsolate;
-var \$globalState;
-var \$globals;
-var \$isWorker;
-var \$supportsWorkers;
-var \$thisScriptUrl;
-function \$static_init(){};
-
-function \$initGlobals(context) {
- context.isolateStatics = new ${namer.ISOLATE}();
-}
-function \$setGlobals(context) {
- $currentIsolate = context.isolateStatics;
- \$globalThis = $currentIsolate;
-}
-$mainEnsureGetter
-""");
- return "${namer.isolateAccess(isolateMain)}($mainAccess)";
- }
-
- emitMain(CodeBuffer buffer) {
- if (compiler.isMockCompilation) return;
- Element main = compiler.mainApp.find(Compiler.MAIN);
- String mainCall = null;
- if (compiler.isolateLibrary != null) {
- Element isolateMain =
- compiler.isolateLibrary.find(Compiler.START_ROOT_ISOLATE);
- mainCall = buildIsolateSetup(buffer, main, isolateMain);
- } else {
- mainCall = '${namer.isolateAccess(main)}()';
- }
- buffer.add("""
-if (typeof document != 'undefined' && document.readyState != 'complete') {
- document.addEventListener('readystatechange', function () {
- if (document.readyState == 'complete') {
- ${mainCall};
- }
- }, false);
-} else {
- ${mainCall};
-}
-""");
- }
-
- String assembleProgram() {
- measure(() {
- mainBuffer.add('function ${namer.ISOLATE}() {}\n');
- mainBuffer.add('init();\n\n');
- // Shorten the code by using "$$" as temporary.
- classesCollector = @"$$";
- mainBuffer.add('var $classesCollector = {};\n');
- // Shorten the code by using [namer.CURRENT_ISOLATE] as temporary.
- isolateProperties = namer.CURRENT_ISOLATE;
- mainBuffer.add('var $isolateProperties = $isolatePropertiesName;\n');
- emitClasses(mainBuffer);
- mainBuffer.add(boundClosureBuffer);
- // Clear the buffer, so that we can reuse it for the native classes.
- boundClosureBuffer.clear();
- emitStaticFunctions(mainBuffer);
- emitStaticFunctionGetters(mainBuffer);
- // We need to finish the classes before we construct compile time
- // constants.
- emitFinishClassesInvocationIfNecessary(mainBuffer);
- emitCompileTimeConstants(mainBuffer);
- // Static field initializations require the classes and compile-time
- // constants to be set up.
- emitStaticNonFinalFieldInitializations(mainBuffer);
-
- isolateProperties = isolatePropertiesName;
- // The following code should not use the short-hand for the
- // initialStatics.
- mainBuffer.add('var ${namer.CURRENT_ISOLATE} = null;\n');
- mainBuffer.add(boundClosureBuffer);
- emitFinishClassesInvocationIfNecessary(mainBuffer);
- // After this assignment we will produce invalid JavaScript code if we use
- // the classesCollector variable.
- classesCollector = 'classesCollector should not be used from now on';
-
- emitFinishIsolateConstructorInvocation(mainBuffer);
- mainBuffer.add(
- 'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n');
-
- nativeEmitter.assembleCode(mainBuffer);
- emitMain(mainBuffer);
- mainBuffer.add('function init() {\n');
- mainBuffer.add('$isolateProperties = {};\n');
- addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer);
- emitFinishIsolateConstructor(mainBuffer);
- mainBuffer.add('}\n');
- compiler.assembledCode = mainBuffer.toString();
-
- if (generateSourceMap) {
- SourceFile compiledFile = new SourceFile(null, compiler.assembledCode);
- String sourceMap = sourceMapBuilder.build(compiledFile);
- // TODO(podivilov): We should find a better way to return source maps to
- // compiler. Using diagnostic handler for that purpose is a temporary
- // hack.
- compiler.reportDiagnostic(
- null, sourceMap, new api.Diagnostic(-1, 'source map'));
- }
- });
- return compiler.assembledCode;
- }
-
- void addMappings(CodeBuffer buffer, int bufferOffset) {
- buffer.forEachSourceLocation((Element element, Token token, int offset) {
- SourceFile sourceFile = element.getCompilationUnit().script.file;
- String sourceName = null;
- if (token.kind === IDENTIFIER_TOKEN) {
- sourceName = token.slowToString();
- }
- int totalOffset = bufferOffset + offset;
- sourceMapBuilder.addMapping(
- sourceFile, token.charOffset, sourceName, totalOffset);
- });
- }
-}
-
-typedef void DefineMemberFunction(String invocationName, CodeBuffer definition);
« no previous file with comments | « lib/compiler/implementation/compiler.dart ('k') | lib/compiler/implementation/js_backend/backend.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698