| OLD | NEW | 
 | (Empty) | 
|    1 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file |  | 
|    2 // for details. All rights reserved. Use of this source code is governed by a |  | 
|    3 // BSD-style license that can be found in the LICENSE file. |  | 
|    4  |  | 
|    5 /** |  | 
|    6  * A function element that represents a closure call. The signature is copied |  | 
|    7  * from the given element. |  | 
|    8  */ |  | 
|    9 class ClosureInvocationElement extends FunctionElement { |  | 
|   10   ClosureInvocationElement(SourceString name, |  | 
|   11                            FunctionElement other) |  | 
|   12       : super.from(name, other, other.enclosingElement); |  | 
|   13  |  | 
|   14   isInstanceMember() => true; |  | 
|   15 } |  | 
|   16  |  | 
|   17 /** |  | 
|   18  * Generates the code for all used classes in the program. Static fields (even |  | 
|   19  * in classes) are ignored, since they can be treated as non-class elements. |  | 
|   20  * |  | 
|   21  * The code for the containing (used) methods must exist in the [:universe:]. |  | 
|   22  */ |  | 
|   23 class CodeEmitterTask extends CompilerTask { |  | 
|   24   static final String INHERIT_FUNCTION = ''' |  | 
|   25 function(child, parent) { |  | 
|   26   if (child.prototype.__proto__) { |  | 
|   27     child.prototype.__proto__ = parent.prototype; |  | 
|   28   } else { |  | 
|   29     function tmp() {}; |  | 
|   30     tmp.prototype = parent.prototype; |  | 
|   31     child.prototype = new tmp(); |  | 
|   32     child.prototype.constructor = child; |  | 
|   33   } |  | 
|   34 }'''; |  | 
|   35  |  | 
|   36   bool addedInheritFunction = false; |  | 
|   37   final Namer namer; |  | 
|   38   final NativeEmitter nativeEmitter; |  | 
|   39   Set<ClassElement> generatedClasses; |  | 
|   40   StringBuffer mainBuffer; |  | 
|   41  |  | 
|   42   CodeEmitterTask(Compiler compiler) |  | 
|   43       : namer = compiler.namer, |  | 
|   44         nativeEmitter = new NativeEmitter(compiler), |  | 
|   45         generatedClasses = new Set<ClassElement>(), |  | 
|   46         mainBuffer = new StringBuffer(), |  | 
|   47         super(compiler); |  | 
|   48  |  | 
|   49   String get name() => 'CodeEmitter'; |  | 
|   50  |  | 
|   51   String get inheritsName() => '${namer.ISOLATE}.\$inherits'; |  | 
|   52  |  | 
|   53   String get objectClassName() { |  | 
|   54     ClassElement objectClass = |  | 
|   55         compiler.coreLibrary.find(const SourceString('Object')); |  | 
|   56     return namer.isolatePropertyAccess(objectClass); |  | 
|   57   } |  | 
|   58  |  | 
|   59   void addInheritFunctionIfNecessary() { |  | 
|   60     if (addedInheritFunction) return; |  | 
|   61     addedInheritFunction = true; |  | 
|   62     mainBuffer.add('$inheritsName = '); |  | 
|   63     mainBuffer.add(INHERIT_FUNCTION); |  | 
|   64     mainBuffer.add(';\n'); |  | 
|   65   } |  | 
|   66  |  | 
|   67   void addParameterStub(FunctionElement member, |  | 
|   68                         String attachTo(String invocationName), |  | 
|   69                         StringBuffer buffer, |  | 
|   70                         Selector selector, |  | 
|   71                         bool isNative) { |  | 
|   72     FunctionParameters parameters = member.computeParameters(compiler); |  | 
|   73     int positionalArgumentCount = selector.positionalArgumentCount; |  | 
|   74     if (positionalArgumentCount == parameters.parameterCount) { |  | 
|   75       assert(selector.namedArgumentCount == 0); |  | 
|   76       return; |  | 
|   77     } |  | 
|   78     ConstantHandler handler = compiler.constantHandler; |  | 
|   79     List<SourceString> names = selector.getOrderedNamedArguments(); |  | 
|   80  |  | 
|   81     String invocationName = |  | 
|   82         namer.instanceMethodInvocationName(member.getLibrary(), member.name, |  | 
|   83                                            selector); |  | 
|   84     buffer.add('${attachTo(invocationName)} = function('); |  | 
|   85  |  | 
|   86     // The parameters that this stub takes. |  | 
|   87     List<String> parametersBuffer = new List<String>(selector.argumentCount); |  | 
|   88     // The arguments that will be passed to the real method. |  | 
|   89     List<String> argumentsBuffer = new List<String>(parameters.parameterCount); |  | 
|   90  |  | 
|   91     // We fill the lists depending on the selector. For example, |  | 
|   92     // take method foo: |  | 
|   93     //    foo(a, b, [c, d]); |  | 
|   94     // |  | 
|   95     // We may have multiple ways of calling foo: |  | 
|   96     // (1) foo(1, 2, 3, 4) |  | 
|   97     // (2) foo(1, 2); |  | 
|   98     // (3) foo(1, 2, 3); |  | 
|   99     // (4) foo(1, 2, c: 3); |  | 
|  100     // (5) foo(1, 2, d: 4); |  | 
|  101     // (6) foo(1, 2, c: 3, d: 4); |  | 
|  102     // (7) foo(1, 2, d: 4, c: 3); |  | 
|  103     // |  | 
|  104     // What we generate at the call sites are: |  | 
|  105     // (1) foo$4(1, 2, 3, 4) |  | 
|  106     // (2) foo$2(1, 2); |  | 
|  107     // (3) foo$3(1, 2, 3); |  | 
|  108     // (4) foo$3$c(1, 2, 3); |  | 
|  109     // (5) foo$3$d(1, 2, 4); |  | 
|  110     // (6) foo$4$c$d(1, 2, 3, 4); |  | 
|  111     // (7) foo$4$c$d(1, 2, 3, 4); |  | 
|  112     // |  | 
|  113     // The stubs we generate are (expressed in Dart): |  | 
|  114     // (1) No stub generated, call is direct. |  | 
|  115     // (2) foo$2(a, b) => foo$4(a, b, null, null) |  | 
|  116     // (3) foo$3(a, b, c) => foo$4(a, b, c, null) |  | 
|  117     // (4) foo$3$c(a, b, c) => foo$4(a, b, c, null); |  | 
|  118     // (5) foo$3$d(a, b, d) => foo$4(a, b, null, d); |  | 
|  119     // (6) foo$4$c$d(a, b, c, d) => foo$4(a, b, c, d); |  | 
|  120     // (7) Same as (5). |  | 
|  121     // |  | 
|  122     // We need to generate a stub for (5) because the order of the |  | 
|  123     // stub arguments and the real method may be different. |  | 
|  124  |  | 
|  125     int count = 0; |  | 
|  126     int indexOfLastOptionalArgumentInParameters = positionalArgumentCount - 1; |  | 
|  127     parameters.forEachParameter((Element element) { |  | 
|  128       String jsName = JsNames.getValid(element.name.slowToString()); |  | 
|  129       if (count < positionalArgumentCount) { |  | 
|  130         parametersBuffer[count] = jsName; |  | 
|  131         argumentsBuffer[count] = jsName; |  | 
|  132       } else { |  | 
|  133         int index = names.indexOf(element.name); |  | 
|  134         if (index != -1) { |  | 
|  135           indexOfLastOptionalArgumentInParameters = count; |  | 
|  136           // The order of the named arguments is not the same as the |  | 
|  137           // one in the real method (which is in Dart source order). |  | 
|  138           argumentsBuffer[count] = jsName; |  | 
|  139           parametersBuffer[selector.positionalArgumentCount + index] = jsName; |  | 
|  140         } else { |  | 
|  141           Constant value = handler.initialVariableValues[element]; |  | 
|  142           if (value == null) { |  | 
|  143             argumentsBuffer[count] = '(void 0)'; |  | 
|  144           } else { |  | 
|  145             if (!value.isNull()) { |  | 
|  146               // If the value is the null constant, we should not pass it |  | 
|  147               // down to the native method. |  | 
|  148               indexOfLastOptionalArgumentInParameters = count; |  | 
|  149             } |  | 
|  150             argumentsBuffer[count] = |  | 
|  151                 handler.writeJsCode(new StringBuffer(), value).toString(); |  | 
|  152           } |  | 
|  153         } |  | 
|  154       } |  | 
|  155       count++; |  | 
|  156     }); |  | 
|  157     String parametersString = Strings.join(parametersBuffer, ","); |  | 
|  158     buffer.add('$parametersString) {\n'); |  | 
|  159  |  | 
|  160     if (isNative) { |  | 
|  161       nativeEmitter.emitParameterStub( |  | 
|  162           member, invocationName, parametersString, argumentsBuffer, |  | 
|  163           indexOfLastOptionalArgumentInParameters); |  | 
|  164     } else { |  | 
|  165       String arguments = Strings.join(argumentsBuffer, ","); |  | 
|  166       buffer.add('  return this.${namer.getName(member)}($arguments)'); |  | 
|  167   } |  | 
|  168     buffer.add('\n};\n'); |  | 
|  169   } |  | 
|  170  |  | 
|  171   void addParameterStubs(FunctionElement member, |  | 
|  172                          String attachTo(String invocationName), |  | 
|  173                          StringBuffer buffer, |  | 
|  174                          [bool isNative = false]) { |  | 
|  175     Set<Selector> selectors = compiler.universe.invokedNames[member.name]; |  | 
|  176     if (selectors == null) return; |  | 
|  177     FunctionParameters parameters = member.computeParameters(compiler); |  | 
|  178     for (Selector selector in selectors) { |  | 
|  179       if (!selector.applies(parameters)) continue; |  | 
|  180       addParameterStub(member, attachTo, buffer, selector, isNative); |  | 
|  181     } |  | 
|  182   } |  | 
|  183  |  | 
|  184   void addInstanceMember(Element member, |  | 
|  185                          String attachTo(String name), |  | 
|  186                          StringBuffer buffer, |  | 
|  187                          [bool isNative = false]) { |  | 
|  188     // TODO(floitsch): we don't need to deal with members of |  | 
|  189     // uninstantiated classes, that have been overwritten by subclasses. |  | 
|  190  |  | 
|  191     if (member.kind === ElementKind.FUNCTION |  | 
|  192         || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY |  | 
|  193         || member.kind === ElementKind.GETTER |  | 
|  194         || member.kind === ElementKind.SETTER) { |  | 
|  195       if (member.modifiers !== null && member.modifiers.isAbstract()) return; |  | 
|  196       String codeBlock = compiler.universe.generatedCode[member]; |  | 
|  197       if (codeBlock == null) return; |  | 
|  198       buffer.add('${attachTo(namer.getName(member))} = $codeBlock;\n'); |  | 
|  199       codeBlock = compiler.universe.generatedBailoutCode[member]; |  | 
|  200       if (codeBlock !== null) { |  | 
|  201         String name = compiler.namer.getBailoutName(member); |  | 
|  202         buffer.add('${attachTo(name)} = $codeBlock;\n'); |  | 
|  203       } |  | 
|  204       FunctionElement function = member; |  | 
|  205       FunctionParameters parameters = function.computeParameters(compiler); |  | 
|  206       if (!parameters.optionalParameters.isEmpty()) { |  | 
|  207         addParameterStubs(member, attachTo, buffer, isNative: isNative); |  | 
|  208       } |  | 
|  209     } else if (member.kind === ElementKind.FIELD) { |  | 
|  210       // TODO(ngeoffray): Have another class generate the code for the |  | 
|  211       // fields. |  | 
|  212       if ((member.modifiers === null || !member.modifiers.isFinal()) && |  | 
|  213           compiler.universe.invokedSetters.contains(member.name)) { |  | 
|  214         String setterName = namer.setterName(member.getLibrary(), member.name); |  | 
|  215         String name = |  | 
|  216             isNative ? member.name.slowToString() : namer.getName(member); |  | 
|  217         buffer.add('${attachTo(setterName)} = function(v){\n'); |  | 
|  218         buffer.add('  this.$name = v;\n};\n'); |  | 
|  219       } |  | 
|  220       if (compiler.universe.invokedGetters.contains(member.name)) { |  | 
|  221         String getterName = namer.getterName(member.getLibrary(), member.name); |  | 
|  222         String name = |  | 
|  223             isNative ? member.name.slowToString() : namer.getName(member); |  | 
|  224         buffer.add('${attachTo(getterName)} = function(){\n'); |  | 
|  225         buffer.add('  return this.$name;\n};\n'); |  | 
|  226       } |  | 
|  227     } else { |  | 
|  228       compiler.internalError('unexpected kind: "${member.kind}"', |  | 
|  229                              element: member); |  | 
|  230     } |  | 
|  231     emitExtraAccessors(member, attachTo, buffer); |  | 
|  232   } |  | 
|  233  |  | 
|  234   bool generateFieldInits(ClassElement classElement, |  | 
|  235                           StringBuffer argumentsBuffer, |  | 
|  236                           StringBuffer bodyBuffer) { |  | 
|  237     bool isFirst = true; |  | 
|  238     do { |  | 
|  239       // TODO(floitsch): make sure there are no name clashes. |  | 
|  240       String className = namer.getName(classElement); |  | 
|  241  |  | 
|  242       void generateFieldInit(Element member) { |  | 
|  243         if (member.isInstanceMember() && member.kind == ElementKind.FIELD) { |  | 
|  244           if (!isFirst) argumentsBuffer.add(', '); |  | 
|  245           isFirst = false; |  | 
|  246           String memberName = namer.instanceFieldName(member.getLibrary(), |  | 
|  247                                                       member.name); |  | 
|  248           argumentsBuffer.add('${className}_$memberName'); |  | 
|  249           bodyBuffer.add('  this.$memberName = ${className}_$memberName;\n'); |  | 
|  250         } |  | 
|  251       } |  | 
|  252  |  | 
|  253       for (Element element in classElement.members) { |  | 
|  254         generateFieldInit(element); |  | 
|  255       } |  | 
|  256       for (Element element in classElement.backendMembers) { |  | 
|  257         generateFieldInit(element); |  | 
|  258       } |  | 
|  259  |  | 
|  260       classElement = classElement.superclass; |  | 
|  261     } while(classElement !== null); |  | 
|  262   } |  | 
|  263  |  | 
|  264   void emitInherits(ClassElement cls, StringBuffer buffer) { |  | 
|  265     ClassElement superclass = cls.superclass; |  | 
|  266     if (superclass !== null) { |  | 
|  267       addInheritFunctionIfNecessary(); |  | 
|  268       String className = namer.isolatePropertyAccess(cls); |  | 
|  269       String superName = namer.isolatePropertyAccess(superclass); |  | 
|  270       buffer.add('${inheritsName}($className, $superName);\n'); |  | 
|  271     } |  | 
|  272   } |  | 
|  273  |  | 
|  274   void ensureGenerated(ClassElement classElement, StringBuffer buffer) { |  | 
|  275     if (classElement == null) return; |  | 
|  276     if (generatedClasses.contains(classElement)) return; |  | 
|  277     generatedClasses.add(classElement); |  | 
|  278     generateClass(classElement, buffer); |  | 
|  279   } |  | 
|  280  |  | 
|  281   void generateClass(ClassElement classElement, StringBuffer buffer) { |  | 
|  282     ensureGenerated(classElement.superclass, buffer); |  | 
|  283  |  | 
|  284     if (classElement.isNative()) { |  | 
|  285       nativeEmitter.generateNativeClass(classElement); |  | 
|  286       return; |  | 
|  287     } else { |  | 
|  288       // TODO(ngeoffray): Instead of switching between buffer, we |  | 
|  289       // should create code sections, and decide where to emit them at |  | 
|  290       // the end. |  | 
|  291       buffer = mainBuffer; |  | 
|  292     } |  | 
|  293  |  | 
|  294     String className = namer.isolatePropertyAccess(classElement); |  | 
|  295     String constructorName = namer.safeName(classElement.name.slowToString()); |  | 
|  296     buffer.add('$className = function $constructorName('); |  | 
|  297     StringBuffer bodyBuffer = new StringBuffer(); |  | 
|  298     // If the class is never instantiated we still need to set it up for |  | 
|  299     // inheritance purposes, but we can leave its JavaScript constructor empty. |  | 
|  300     if (compiler.universe.instantiatedClasses.contains(classElement)) { |  | 
|  301       generateFieldInits(classElement, buffer, bodyBuffer); |  | 
|  302     } |  | 
|  303     buffer.add(') {\n'); |  | 
|  304     buffer.add(bodyBuffer); |  | 
|  305     buffer.add('};\n'); |  | 
|  306  |  | 
|  307     emitInherits(classElement, buffer); |  | 
|  308  |  | 
|  309     String attachTo(String name) => '$className.prototype.$name'; |  | 
|  310     for (Element member in classElement.members) { |  | 
|  311       if (member.isInstanceMember()) { |  | 
|  312         addInstanceMember(member, attachTo, buffer); |  | 
|  313       } |  | 
|  314     } |  | 
|  315     for (Element member in classElement.backendMembers) { |  | 
|  316       if (member.isInstanceMember()) { |  | 
|  317         addInstanceMember(member, attachTo, buffer); |  | 
|  318       } |  | 
|  319     } |  | 
|  320     generateTypeTests(classElement, (Element other) { |  | 
|  321       buffer.add('${attachTo(namer.operatorIs(other))} = '); |  | 
|  322       if (nativeEmitter.requiresNativeIsCheck(other)) { |  | 
|  323         buffer.add('function() { return true; }'); |  | 
|  324       } else { |  | 
|  325         buffer.add('true'); |  | 
|  326       } |  | 
|  327       buffer.add(';\n'); |  | 
|  328     }); |  | 
|  329  |  | 
|  330     if (classElement === compiler.objectClass && compiler.enabledNoSuchMethod) { |  | 
|  331       // Emit the noSuchMethods on the Object prototype now, so that |  | 
|  332       // the code in the dynamicMethod can find them. Note that the |  | 
|  333       // code in dynamicMethod is invoked before analyzing the full JS |  | 
|  334       // script. |  | 
|  335       emitNoSuchMethodCalls(buffer); |  | 
|  336     } |  | 
|  337   } |  | 
|  338  |  | 
|  339   void generateTypeTests(ClassElement cls, |  | 
|  340                          void generateTypeTest(ClassElement element)) { |  | 
|  341     if (compiler.universe.isChecks.contains(cls)) { |  | 
|  342       generateTypeTest(cls); |  | 
|  343     } |  | 
|  344     generateInterfacesIsTests(cls, generateTypeTest, new Set<Element>()); |  | 
|  345   } |  | 
|  346  |  | 
|  347   void generateInterfacesIsTests(ClassElement cls, |  | 
|  348                                  void generateTypeTest(ClassElement element), |  | 
|  349                                  Set<Element> alreadyGenerated) { |  | 
|  350     for (Type interfaceType in cls.interfaces) { |  | 
|  351       Element element = interfaceType.element; |  | 
|  352       if (!alreadyGenerated.contains(element) && |  | 
|  353           compiler.universe.isChecks.contains(element)) { |  | 
|  354         alreadyGenerated.add(element); |  | 
|  355         generateTypeTest(element); |  | 
|  356       } |  | 
|  357       generateInterfacesIsTests(element, generateTypeTest, alreadyGenerated); |  | 
|  358     } |  | 
|  359   } |  | 
|  360  |  | 
|  361   void emitClasses(StringBuffer buffer) { |  | 
|  362     for (ClassElement element in compiler.universe.instantiatedClasses) { |  | 
|  363       ensureGenerated(element, buffer); |  | 
|  364     } |  | 
|  365   } |  | 
|  366  |  | 
|  367   void emitStaticFunctionsWithNamer(StringBuffer buffer, |  | 
|  368                                     Map<Element, String> generatedCode, |  | 
|  369                                     String functionNamer(Element element)) { |  | 
|  370     generatedCode.forEach((Element element, String codeBlock) { |  | 
|  371       if (!element.isInstanceMember()) { |  | 
|  372         buffer.add('${functionNamer(element)} = '); |  | 
|  373         buffer.add(codeBlock); |  | 
|  374         buffer.add(';\n\n'); |  | 
|  375       } |  | 
|  376     }); |  | 
|  377   } |  | 
|  378  |  | 
|  379   void emitStaticFunctions(StringBuffer buffer) { |  | 
|  380     emitStaticFunctionsWithNamer(buffer, |  | 
|  381                                  compiler.universe.generatedCode, |  | 
|  382                                  namer.isolatePropertyAccess); |  | 
|  383     emitStaticFunctionsWithNamer(buffer, |  | 
|  384                                  compiler.universe.generatedBailoutCode, |  | 
|  385                                  namer.isolateBailoutPropertyAccess); |  | 
|  386   } |  | 
|  387  |  | 
|  388   void emitStaticFunctionGetters(StringBuffer buffer) { |  | 
|  389     Set<FunctionElement> functionsNeedingGetter = |  | 
|  390         compiler.universe.staticFunctionsNeedingGetter; |  | 
|  391     for (FunctionElement element in functionsNeedingGetter) { |  | 
|  392       // The static function does not have the correct name. Since |  | 
|  393       // [addParameterStubs] use the name to create its stubs we simply |  | 
|  394       // create a fake element with the correct name. |  | 
|  395       // Note: the callElement will not have any enclosingElement. |  | 
|  396       FunctionElement callElement = |  | 
|  397           new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, element); |  | 
|  398       String staticName = namer.isolatePropertyAccess(element); |  | 
|  399       int parameterCount = element.parameterCount(compiler); |  | 
|  400       String invocationName = |  | 
|  401           namer.instanceMethodName(element.getLibrary(), callElement.name, |  | 
|  402                                    parameterCount); |  | 
|  403       buffer.add("$staticName.$invocationName = $staticName;\n"); |  | 
|  404       addParameterStubs(callElement, (name) => '$staticName.$name', buffer); |  | 
|  405     } |  | 
|  406   } |  | 
|  407  |  | 
|  408   void emitDynamicFunctionGetter(StringBuffer buffer, |  | 
|  409                                  String attachTo(String invocationName), |  | 
|  410                                  FunctionElement member) { |  | 
|  411     // For every method that has the same name as a property-get we create a |  | 
|  412     // getter that returns a bound closure. Say we have a class 'A' with method |  | 
|  413     // 'foo' and somewhere in the code there is a dynamic property get of |  | 
|  414     // 'foo'. Then we generate the following code (in pseudo Dart): |  | 
|  415     // |  | 
|  416     // class A { |  | 
|  417     //    foo(x, y, z) { ... } // Original function. |  | 
|  418     //    get foo() { return new BoundClosure499(this); } |  | 
|  419     // } |  | 
|  420     // class BoundClosure499 extends Closure { |  | 
|  421     //   var self; |  | 
|  422     //   BoundClosure499(this.self); |  | 
|  423     //   $call3(x, y, z) { return self.foo(x, y, z); } |  | 
|  424     // } |  | 
|  425  |  | 
|  426     // TODO(floitsch): share the closure classes with other classes |  | 
|  427     // if they share methods with the same signature. |  | 
|  428  |  | 
|  429     // The closure class. |  | 
|  430     SourceString name = const SourceString("BoundClosure"); |  | 
|  431     ClassElement closureClassElement = |  | 
|  432         new ClosureClassElement(compiler, member.getCompilationUnit()); |  | 
|  433     String isolateAccess = namer.isolatePropertyAccess(closureClassElement); |  | 
|  434     ensureGenerated(closureClassElement.superclass, buffer); |  | 
|  435  |  | 
|  436     // Define the constructor with a name so that Object.toString can |  | 
|  437     // find the class name of the closure class. |  | 
|  438     buffer.add("$isolateAccess = function $name(self) "); |  | 
|  439     buffer.add("{ this.self = self; };\n"); |  | 
|  440     emitInherits(closureClassElement, buffer); |  | 
|  441  |  | 
|  442     String prototype = "$isolateAccess.prototype"; |  | 
|  443  |  | 
|  444     // Now add the methods on the closure class. The instance method does not |  | 
|  445     // have the correct name. Since [addParameterStubs] use the name to create |  | 
|  446     // its stubs we simply create a fake element with the correct name. |  | 
|  447     // Note: the callElement will not have any enclosingElement. |  | 
|  448     FunctionElement callElement = |  | 
|  449         new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, member); |  | 
|  450  |  | 
|  451     int parameterCount = member.parameterCount(compiler); |  | 
|  452     String invocationName = |  | 
|  453         namer.instanceMethodName(member.getLibrary(), |  | 
|  454                                  callElement.name, parameterCount); |  | 
|  455     String targetName = namer.instanceMethodName(member.getLibrary(), |  | 
|  456                                                  member.name, parameterCount); |  | 
|  457     List<String> arguments = new List<String>(parameterCount); |  | 
|  458     for (int i = 0; i < parameterCount; i++) { |  | 
|  459       arguments[i] = "arg$i"; |  | 
|  460     } |  | 
|  461     String joinedArgs = Strings.join(arguments, ", "); |  | 
|  462     buffer.add("$prototype.$invocationName = function($joinedArgs) {\n"); |  | 
|  463     buffer.add("  return this.self.$targetName($joinedArgs);\n"); |  | 
|  464     buffer.add("};\n"); |  | 
|  465     addParameterStubs(callElement, |  | 
|  466                       (invocationName) => '$prototype.$invocationName', |  | 
|  467                       buffer); |  | 
|  468  |  | 
|  469     // And finally the getter. |  | 
|  470     String getterName = namer.getterName(member.getLibrary(), member.name); |  | 
|  471     String closureClass = namer.isolateAccess(closureClassElement); |  | 
|  472     buffer.add("${attachTo(getterName)} = function() {\n"); |  | 
|  473     buffer.add("  return new $closureClass(this);\n"); |  | 
|  474     buffer.add("};\n"); |  | 
|  475   } |  | 
|  476  |  | 
|  477   void emitCallStubForGetter(StringBuffer buffer, |  | 
|  478                              String attachTo(String name), |  | 
|  479                              Element member, |  | 
|  480                              Set<Selector> selectors) { |  | 
|  481     String getter; |  | 
|  482     if (member.kind == ElementKind.GETTER) { |  | 
|  483       getter = "this.${namer.getterName(member.getLibrary(), member.name)}()"; |  | 
|  484     } else { |  | 
|  485       getter = |  | 
|  486           "this.${namer.instanceFieldName(member.getLibrary(), member.name)}"; |  | 
|  487     } |  | 
|  488     for (Selector selector in selectors) { |  | 
|  489       String invocationName = |  | 
|  490           namer.instanceMethodInvocationName(member.getLibrary(), member.name, |  | 
|  491                                              selector); |  | 
|  492       SourceString callName = Namer.CLOSURE_INVOCATION_NAME; |  | 
|  493       String closureCallName = |  | 
|  494           namer.instanceMethodInvocationName(member.getLibrary(), callName, |  | 
|  495                                              selector); |  | 
|  496       List<String> arguments = <String>[]; |  | 
|  497       for (int i = 0; i < selector.argumentCount; i++) { |  | 
|  498         arguments.add("arg$i"); |  | 
|  499       } |  | 
|  500       String joined = Strings.join(arguments, ", "); |  | 
|  501       buffer.add("${attachTo(invocationName)} = function($joined) {\n"); |  | 
|  502       buffer.add("  return $getter.$closureCallName($joined);\n"); |  | 
|  503       buffer.add("};\n"); |  | 
|  504     } |  | 
|  505   } |  | 
|  506  |  | 
|  507   void emitStaticNonFinalFieldInitializations(StringBuffer buffer) { |  | 
|  508     // Adds initializations inside the Isolate constructor. |  | 
|  509     // Example: |  | 
|  510     //    function Isolate() { |  | 
|  511     //       this.staticNonFinal = Isolate.prototype.someVal; |  | 
|  512     //       ... |  | 
|  513     //    } |  | 
|  514     ConstantHandler handler = compiler.constantHandler; |  | 
|  515     List<VariableElement> staticNonFinalFields = |  | 
|  516         handler.getStaticNonFinalFieldsForEmission(); |  | 
|  517     if (!staticNonFinalFields.isEmpty()) buffer.add('\n'); |  | 
|  518     for (Element element in staticNonFinalFields) { |  | 
|  519       buffer.add('  this.${namer.getName(element)} = '); |  | 
|  520       compiler.withCurrentElement(element, () { |  | 
|  521           handler.writeJsCodeForVariable(buffer, element); |  | 
|  522         }); |  | 
|  523       buffer.add(';\n'); |  | 
|  524     } |  | 
|  525   } |  | 
|  526  |  | 
|  527   void emitCompileTimeConstants(StringBuffer buffer) { |  | 
|  528     ConstantHandler handler = compiler.constantHandler; |  | 
|  529     List<Constant> constants = handler.getConstantsForEmission(); |  | 
|  530     String prototype = "${namer.ISOLATE}.prototype"; |  | 
|  531     bool addedMakeConstantList = false; |  | 
|  532     for (Constant constant in constants) { |  | 
|  533       if (!addedMakeConstantList && constant.isList()) { |  | 
|  534         addedMakeConstantList = true; |  | 
|  535         emitMakeConstantList(prototype, buffer); |  | 
|  536       } |  | 
|  537       String name = handler.getNameForConstant(constant); |  | 
|  538       buffer.add('$prototype.$name = '); |  | 
|  539       handler.writeJsCode(buffer, constant); |  | 
|  540       buffer.add(';\n'); |  | 
|  541     } |  | 
|  542   } |  | 
|  543  |  | 
|  544   void emitMakeConstantList(String prototype, StringBuffer buffer) { |  | 
|  545     buffer.add(prototype); |  | 
|  546     buffer.add(@'''.makeConstantList = function(list) { |  | 
|  547   list.immutable$list = true; |  | 
|  548   list.fixed$length = true; |  | 
|  549   return list; |  | 
|  550 }; |  | 
|  551 '''); |  | 
|  552   } |  | 
|  553  |  | 
|  554   void emitStaticFinalFieldInitializations(StringBuffer buffer) { |  | 
|  555     ConstantHandler handler = compiler.constantHandler; |  | 
|  556     List<VariableElement> staticFinalFields = |  | 
|  557         handler.getStaticFinalFieldsForEmission(); |  | 
|  558     for (VariableElement element in staticFinalFields) { |  | 
|  559       buffer.add('${namer.isolatePropertyAccess(element)} = '); |  | 
|  560       compiler.withCurrentElement(element, () { |  | 
|  561           handler.writeJsCodeForVariable(buffer, element); |  | 
|  562         }); |  | 
|  563       buffer.add(';\n'); |  | 
|  564     } |  | 
|  565   } |  | 
|  566  |  | 
|  567   void emitExtraAccessors(Element member, |  | 
|  568                           String attachTo(String name), |  | 
|  569                           StringBuffer buffer) { |  | 
|  570     if (member.kind == ElementKind.GETTER || member.kind == ElementKind.FIELD) { |  | 
|  571       Set<Selector> selectors = compiler.universe.invokedNames[member.name]; |  | 
|  572       if (selectors !== null && !selectors.isEmpty()) { |  | 
|  573         compiler.emitter.emitCallStubForGetter( |  | 
|  574             buffer, attachTo, member, selectors); |  | 
|  575       } |  | 
|  576     } else if (member.kind == ElementKind.FUNCTION) { |  | 
|  577       if (compiler.universe.invokedGetters.contains(member.name)) { |  | 
|  578         compiler.emitter.emitDynamicFunctionGetter( |  | 
|  579             buffer, attachTo, member); |  | 
|  580       } |  | 
|  581     } |  | 
|  582   } |  | 
|  583  |  | 
|  584   void emitNoSuchMethodCalls(StringBuffer buffer) { |  | 
|  585     // Do not generate no such method calls if there is no class. |  | 
|  586     if (compiler.universe.instantiatedClasses.isEmpty()) return; |  | 
|  587  |  | 
|  588     ClassElement objectClass = |  | 
|  589         compiler.coreLibrary.find(const SourceString('Object')); |  | 
|  590     String className = namer.isolatePropertyAccess(objectClass); |  | 
|  591     String prototype = '$className.prototype'; |  | 
|  592     String noSuchMethodName = |  | 
|  593         namer.instanceMethodName(null, Compiler.NO_SUCH_METHOD, 2); |  | 
|  594     Collection<LibraryElement> libraries = |  | 
|  595         compiler.universe.libraries.getValues(); |  | 
|  596  |  | 
|  597     void generateMethod(String methodName, String jsName, Selector selector) { |  | 
|  598       buffer.add('$prototype.$jsName = function'); |  | 
|  599       StringBuffer args = new StringBuffer(); |  | 
|  600       for (int i = 0; i < selector.argumentCount; i++) { |  | 
|  601         if (i != 0) args.add(', '); |  | 
|  602         args.add('arg$i'); |  | 
|  603       } |  | 
|  604       // We need to check if the object has a noSuchMethod. If not, it |  | 
|  605       // means the object is a native object, and we can just call our |  | 
|  606       // generic noSuchMethod. Note that when calling this method, the |  | 
|  607       // 'this' object is not a Dart object. |  | 
|  608       buffer.add(' ($args) {\n'); |  | 
|  609       buffer.add('  return this.$noSuchMethodName\n'); |  | 
|  610       buffer.add("      ? this.$noSuchMethodName('$methodName', [$args])\n"); |  | 
|  611       buffer.add("      : $objectClassName.prototype.$noSuchMethodName.call("); |  | 
|  612       buffer.add("this, '$methodName', [$args])\n"); |  | 
|  613       buffer.add('}\n'); |  | 
|  614     } |  | 
|  615  |  | 
|  616     compiler.universe.invokedNames.forEach((SourceString methodName, |  | 
|  617                                             Set<Selector> selectors) { |  | 
|  618       if (objectClass.lookupLocalMember(methodName) === null |  | 
|  619           && methodName != Namer.OPERATOR_EQUALS) { |  | 
|  620         for (Selector selector in selectors) { |  | 
|  621           if (methodName.isPrivate()) { |  | 
|  622             for (LibraryElement lib in libraries) { |  | 
|  623               String jsName = |  | 
|  624                 namer.instanceMethodInvocationName(lib, methodName, selector); |  | 
|  625               generateMethod(methodName.slowToString(), jsName, selector); |  | 
|  626             } |  | 
|  627           } else { |  | 
|  628             String jsName = |  | 
|  629               namer.instanceMethodInvocationName(null, methodName, selector); |  | 
|  630             generateMethod(methodName.slowToString(), jsName, selector); |  | 
|  631           } |  | 
|  632         } |  | 
|  633       } |  | 
|  634     }); |  | 
|  635  |  | 
|  636     compiler.universe.invokedGetters.forEach((SourceString getterName) { |  | 
|  637       if (getterName.isPrivate()) { |  | 
|  638         for (LibraryElement lib in libraries) { |  | 
|  639           String jsName = namer.getterName(lib, getterName); |  | 
|  640           generateMethod('get ${getterName.slowToString()}', jsName, |  | 
|  641                          Selector.GETTER); |  | 
|  642         } |  | 
|  643       } else { |  | 
|  644         String jsName = namer.getterName(null, getterName); |  | 
|  645         generateMethod('get ${getterName.slowToString()}', jsName, |  | 
|  646                        Selector.GETTER); |  | 
|  647       } |  | 
|  648     }); |  | 
|  649  |  | 
|  650     compiler.universe.invokedSetters.forEach((SourceString setterName) { |  | 
|  651       if (setterName.isPrivate()) { |  | 
|  652         for (LibraryElement lib in libraries) { |  | 
|  653           String jsName = namer.setterName(lib, setterName); |  | 
|  654           generateMethod('set ${setterName.slowToString()}', jsName, |  | 
|  655                          Selector.SETTER); |  | 
|  656         } |  | 
|  657       } else { |  | 
|  658         String jsName = namer.setterName(null, setterName); |  | 
|  659         generateMethod('set ${setterName.slowToString()}', jsName, |  | 
|  660                        Selector.SETTER); |  | 
|  661       } |  | 
|  662     }); |  | 
|  663   } |  | 
|  664  |  | 
|  665   String buildIsolateSetup(Element appMain, Element isolateMain) { |  | 
|  666     String mainAccess = "${namer.isolateAccess(appMain)}"; |  | 
|  667     String currentIsolate = "${namer.CURRENT_ISOLATE}"; |  | 
|  668     String mainEnsureGetter = ''; |  | 
|  669     // Since we pass the closurized version of the main method to |  | 
|  670     // the isolate method, we must make sure that it exists. |  | 
|  671     if (!compiler.universe.staticFunctionsNeedingGetter.contains(appMain)) { |  | 
|  672       String invocationName = |  | 
|  673           "${namer.closureInvocationName(Selector.INVOCATION_0)}"; |  | 
|  674       mainEnsureGetter = "$mainAccess.$invocationName = $mainAccess"; |  | 
|  675     } |  | 
|  676  |  | 
|  677     // TODO(ngeoffray): These globals are currently required by the isolate |  | 
|  678     // library, but since leg already generates code on an Isolate object, they |  | 
|  679     // are not really needed. We should remove them once Leg replaces Frog. |  | 
|  680     return """ |  | 
|  681 var \$globalThis = $currentIsolate; |  | 
|  682 var \$globalState; |  | 
|  683 var \$globals; |  | 
|  684 function \$static_init(){}; |  | 
|  685  |  | 
|  686 function \$initGlobals(context) { |  | 
|  687   context.isolateStatics = new ${namer.ISOLATE}(); |  | 
|  688 } |  | 
|  689 function \$setGlobals(context) { |  | 
|  690   $currentIsolate = context.isolateStatics; |  | 
|  691   \$globalThis = $currentIsolate; |  | 
|  692 } |  | 
|  693 $mainEnsureGetter |  | 
|  694 ${namer.isolateAccess(isolateMain)}($mainAccess);"""; |  | 
|  695   } |  | 
|  696  |  | 
|  697   emitMain(StringBuffer buffer) { |  | 
|  698     if (compiler.isMockCompilation) return; |  | 
|  699     Element main = compiler.mainApp.find(Compiler.MAIN); |  | 
|  700     if (compiler.isolateLibrary != null) { |  | 
|  701       Element isolateMain = |  | 
|  702         compiler.isolateLibrary.find(Compiler.START_ROOT_ISOLATE); |  | 
|  703       buffer.add(buildIsolateSetup(main, isolateMain)); |  | 
|  704     } else { |  | 
|  705       buffer.add('${namer.isolateAccess(main)}();\n'); |  | 
|  706     } |  | 
|  707   } |  | 
|  708  |  | 
|  709   String assembleProgram() { |  | 
|  710     measure(() { |  | 
|  711       mainBuffer.add('function ${namer.ISOLATE}() {'); |  | 
|  712       emitStaticNonFinalFieldInitializations(mainBuffer); |  | 
|  713       mainBuffer.add('}\n\n'); |  | 
|  714       emitClasses(mainBuffer); |  | 
|  715       emitStaticFunctions(mainBuffer); |  | 
|  716       emitStaticFunctionGetters(mainBuffer); |  | 
|  717       emitCompileTimeConstants(mainBuffer); |  | 
|  718       emitStaticFinalFieldInitializations(mainBuffer); |  | 
|  719       nativeEmitter.emitDynamicDispatchMetadata(); |  | 
|  720       mainBuffer.add( |  | 
|  721           'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n'); |  | 
|  722       nativeEmitter.assembleCode(mainBuffer); |  | 
|  723       emitMain(mainBuffer); |  | 
|  724       compiler.assembledCode = mainBuffer.toString(); |  | 
|  725     }); |  | 
|  726     return compiler.assembledCode; |  | 
|  727   } |  | 
|  728 } |  | 
| OLD | NEW |