| Index: frog/leg/emitter.dart
 | 
| ===================================================================
 | 
| --- frog/leg/emitter.dart	(revision 5925)
 | 
| +++ frog/leg/emitter.dart	(working copy)
 | 
| @@ -1,728 +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 {
 | 
| -  static final String INHERIT_FUNCTION = '''
 | 
| -function(child, parent) {
 | 
| -  if (child.prototype.__proto__) {
 | 
| -    child.prototype.__proto__ = parent.prototype;
 | 
| -  } else {
 | 
| -    function tmp() {};
 | 
| -    tmp.prototype = parent.prototype;
 | 
| -    child.prototype = new tmp();
 | 
| -    child.prototype.constructor = child;
 | 
| -  }
 | 
| -}''';
 | 
| -
 | 
| -  bool addedInheritFunction = false;
 | 
| -  final Namer namer;
 | 
| -  final NativeEmitter nativeEmitter;
 | 
| -  Set<ClassElement> generatedClasses;
 | 
| -  StringBuffer mainBuffer;
 | 
| -
 | 
| -  CodeEmitterTask(Compiler compiler)
 | 
| -      : namer = compiler.namer,
 | 
| -        nativeEmitter = new NativeEmitter(compiler),
 | 
| -        generatedClasses = new Set<ClassElement>(),
 | 
| -        mainBuffer = new StringBuffer(),
 | 
| -        super(compiler);
 | 
| -
 | 
| -  String get name() => 'CodeEmitter';
 | 
| -
 | 
| -  String get inheritsName() => '${namer.ISOLATE}.\$inherits';
 | 
| -
 | 
| -  String get objectClassName() {
 | 
| -    ClassElement objectClass =
 | 
| -        compiler.coreLibrary.find(const SourceString('Object'));
 | 
| -    return namer.isolatePropertyAccess(objectClass);
 | 
| -  }
 | 
| -
 | 
| -  void addInheritFunctionIfNecessary() {
 | 
| -    if (addedInheritFunction) return;
 | 
| -    addedInheritFunction = true;
 | 
| -    mainBuffer.add('$inheritsName = ');
 | 
| -    mainBuffer.add(INHERIT_FUNCTION);
 | 
| -    mainBuffer.add(';\n');
 | 
| -  }
 | 
| -
 | 
| -  void addParameterStub(FunctionElement member,
 | 
| -                        String attachTo(String invocationName),
 | 
| -                        StringBuffer buffer,
 | 
| -                        Selector selector,
 | 
| -                        bool isNative) {
 | 
| -    FunctionParameters parameters = member.computeParameters(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);
 | 
| -    buffer.add('${attachTo(invocationName)} = 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] = '(void 0)';
 | 
| -          } else {
 | 
| -            if (!value.isNull()) {
 | 
| -              // If the value is the null constant, we should not pass it
 | 
| -              // down to the native method.
 | 
| -              indexOfLastOptionalArgumentInParameters = count;
 | 
| -            }
 | 
| -            argumentsBuffer[count] =
 | 
| -                handler.writeJsCode(new StringBuffer(), value).toString();
 | 
| -          }
 | 
| -        }
 | 
| -      }
 | 
| -      count++;
 | 
| -    });
 | 
| -    String parametersString = Strings.join(parametersBuffer, ",");
 | 
| -    buffer.add('$parametersString) {\n');
 | 
| -
 | 
| -    if (isNative) {
 | 
| -      nativeEmitter.emitParameterStub(
 | 
| -          member, invocationName, parametersString, argumentsBuffer,
 | 
| -          indexOfLastOptionalArgumentInParameters);
 | 
| -    } else {
 | 
| -      String arguments = Strings.join(argumentsBuffer, ",");
 | 
| -      buffer.add('  return this.${namer.getName(member)}($arguments)');
 | 
| -  }
 | 
| -    buffer.add('\n};\n');
 | 
| -  }
 | 
| -
 | 
| -  void addParameterStubs(FunctionElement member,
 | 
| -                         String attachTo(String invocationName),
 | 
| -                         StringBuffer buffer,
 | 
| -                         [bool isNative = false]) {
 | 
| -    Set<Selector> selectors = compiler.universe.invokedNames[member.name];
 | 
| -    if (selectors == null) return;
 | 
| -    FunctionParameters parameters = member.computeParameters(compiler);
 | 
| -    for (Selector selector in selectors) {
 | 
| -      if (!selector.applies(parameters)) continue;
 | 
| -      addParameterStub(member, attachTo, buffer, selector, isNative);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void addInstanceMember(Element member,
 | 
| -                         String attachTo(String name),
 | 
| -                         StringBuffer buffer,
 | 
| -                         [bool isNative = false]) {
 | 
| -    // 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;
 | 
| -      String codeBlock = compiler.universe.generatedCode[member];
 | 
| -      if (codeBlock == null) return;
 | 
| -      buffer.add('${attachTo(namer.getName(member))} = $codeBlock;\n');
 | 
| -      codeBlock = compiler.universe.generatedBailoutCode[member];
 | 
| -      if (codeBlock !== null) {
 | 
| -        String name = compiler.namer.getBailoutName(member);
 | 
| -        buffer.add('${attachTo(name)} = $codeBlock;\n');
 | 
| -      }
 | 
| -      FunctionElement function = member;
 | 
| -      FunctionParameters parameters = function.computeParameters(compiler);
 | 
| -      if (!parameters.optionalParameters.isEmpty()) {
 | 
| -        addParameterStubs(member, attachTo, buffer, isNative: isNative);
 | 
| -      }
 | 
| -    } else if (member.kind === ElementKind.FIELD) {
 | 
| -      // TODO(ngeoffray): Have another class generate the code for the
 | 
| -      // fields.
 | 
| -      if ((member.modifiers === null || !member.modifiers.isFinal()) &&
 | 
| -          compiler.universe.invokedSetters.contains(member.name)) {
 | 
| -        String setterName = namer.setterName(member.getLibrary(), member.name);
 | 
| -        String name =
 | 
| -            isNative ? member.name.slowToString() : namer.getName(member);
 | 
| -        buffer.add('${attachTo(setterName)} = function(v){\n');
 | 
| -        buffer.add('  this.$name = v;\n};\n');
 | 
| -      }
 | 
| -      if (compiler.universe.invokedGetters.contains(member.name)) {
 | 
| -        String getterName = namer.getterName(member.getLibrary(), member.name);
 | 
| -        String name =
 | 
| -            isNative ? member.name.slowToString() : namer.getName(member);
 | 
| -        buffer.add('${attachTo(getterName)} = function(){\n');
 | 
| -        buffer.add('  return this.$name;\n};\n');
 | 
| -      }
 | 
| -    } else {
 | 
| -      compiler.internalError('unexpected kind: "${member.kind}"',
 | 
| -                             element: member);
 | 
| -    }
 | 
| -    emitExtraAccessors(member, attachTo, buffer);
 | 
| -  }
 | 
| -
 | 
| -  bool generateFieldInits(ClassElement classElement,
 | 
| -                          StringBuffer argumentsBuffer,
 | 
| -                          StringBuffer bodyBuffer) {
 | 
| -    bool isFirst = true;
 | 
| -    do {
 | 
| -      // TODO(floitsch): make sure there are no name clashes.
 | 
| -      String className = namer.getName(classElement);
 | 
| -
 | 
| -      void generateFieldInit(Element member) {
 | 
| -        if (member.isInstanceMember() && member.kind == ElementKind.FIELD) {
 | 
| -          if (!isFirst) argumentsBuffer.add(', ');
 | 
| -          isFirst = false;
 | 
| -          String memberName = namer.instanceFieldName(member.getLibrary(),
 | 
| -                                                      member.name);
 | 
| -          argumentsBuffer.add('${className}_$memberName');
 | 
| -          bodyBuffer.add('  this.$memberName = ${className}_$memberName;\n');
 | 
| -        }
 | 
| -      }
 | 
| -
 | 
| -      for (Element element in classElement.members) {
 | 
| -        generateFieldInit(element);
 | 
| -      }
 | 
| -      for (Element element in classElement.backendMembers) {
 | 
| -        generateFieldInit(element);
 | 
| -      }
 | 
| -
 | 
| -      classElement = classElement.superclass;
 | 
| -    } while(classElement !== null);
 | 
| -  }
 | 
| -
 | 
| -  void emitInherits(ClassElement cls, StringBuffer buffer) {
 | 
| -    ClassElement superclass = cls.superclass;
 | 
| -    if (superclass !== null) {
 | 
| -      addInheritFunctionIfNecessary();
 | 
| -      String className = namer.isolatePropertyAccess(cls);
 | 
| -      String superName = namer.isolatePropertyAccess(superclass);
 | 
| -      buffer.add('${inheritsName}($className, $superName);\n');
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void ensureGenerated(ClassElement classElement, StringBuffer buffer) {
 | 
| -    if (classElement == null) return;
 | 
| -    if (generatedClasses.contains(classElement)) return;
 | 
| -    generatedClasses.add(classElement);
 | 
| -    generateClass(classElement, buffer);
 | 
| -  }
 | 
| -
 | 
| -  void generateClass(ClassElement classElement, StringBuffer buffer) {
 | 
| -    ensureGenerated(classElement.superclass, 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;
 | 
| -    }
 | 
| -
 | 
| -    String className = namer.isolatePropertyAccess(classElement);
 | 
| -    String constructorName = namer.safeName(classElement.name.slowToString());
 | 
| -    buffer.add('$className = function $constructorName(');
 | 
| -    StringBuffer bodyBuffer = new StringBuffer();
 | 
| -    // If the class is never instantiated we still need to set it up for
 | 
| -    // inheritance purposes, but we can leave its JavaScript constructor empty.
 | 
| -    if (compiler.universe.instantiatedClasses.contains(classElement)) {
 | 
| -      generateFieldInits(classElement, buffer, bodyBuffer);
 | 
| -    }
 | 
| -    buffer.add(') {\n');
 | 
| -    buffer.add(bodyBuffer);
 | 
| -    buffer.add('};\n');
 | 
| -
 | 
| -    emitInherits(classElement, buffer);
 | 
| -
 | 
| -    String attachTo(String name) => '$className.prototype.$name';
 | 
| -    for (Element member in classElement.members) {
 | 
| -      if (member.isInstanceMember()) {
 | 
| -        addInstanceMember(member, attachTo, buffer);
 | 
| -      }
 | 
| -    }
 | 
| -    for (Element member in classElement.backendMembers) {
 | 
| -      if (member.isInstanceMember()) {
 | 
| -        addInstanceMember(member, attachTo, buffer);
 | 
| -      }
 | 
| -    }
 | 
| -    generateTypeTests(classElement, (Element other) {
 | 
| -      buffer.add('${attachTo(namer.operatorIs(other))} = ');
 | 
| -      if (nativeEmitter.requiresNativeIsCheck(other)) {
 | 
| -        buffer.add('function() { return true; }');
 | 
| -      } else {
 | 
| -        buffer.add('true');
 | 
| -      }
 | 
| -      buffer.add(';\n');
 | 
| -    });
 | 
| -
 | 
| -    if (classElement === compiler.objectClass && compiler.enabledNoSuchMethod) {
 | 
| -      // Emit the noSuchMethods on the Object prototype now, so that
 | 
| -      // the code in the dynamicMethod can find them. Note that the
 | 
| -      // code in dynamicMethod is invoked before analyzing the full JS
 | 
| -      // script.
 | 
| -      emitNoSuchMethodCalls(buffer);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void generateTypeTests(ClassElement cls,
 | 
| -                         void generateTypeTest(ClassElement element)) {
 | 
| -    if (compiler.universe.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.universe.isChecks.contains(element)) {
 | 
| -        alreadyGenerated.add(element);
 | 
| -        generateTypeTest(element);
 | 
| -      }
 | 
| -      generateInterfacesIsTests(element, generateTypeTest, alreadyGenerated);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void emitClasses(StringBuffer buffer) {
 | 
| -    for (ClassElement element in compiler.universe.instantiatedClasses) {
 | 
| -      ensureGenerated(element, buffer);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void emitStaticFunctionsWithNamer(StringBuffer buffer,
 | 
| -                                    Map<Element, String> generatedCode,
 | 
| -                                    String functionNamer(Element element)) {
 | 
| -    generatedCode.forEach((Element element, String codeBlock) {
 | 
| -      if (!element.isInstanceMember()) {
 | 
| -        buffer.add('${functionNamer(element)} = ');
 | 
| -        buffer.add(codeBlock);
 | 
| -        buffer.add(';\n\n');
 | 
| -      }
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  void emitStaticFunctions(StringBuffer buffer) {
 | 
| -    emitStaticFunctionsWithNamer(buffer,
 | 
| -                                 compiler.universe.generatedCode,
 | 
| -                                 namer.isolatePropertyAccess);
 | 
| -    emitStaticFunctionsWithNamer(buffer,
 | 
| -                                 compiler.universe.generatedBailoutCode,
 | 
| -                                 namer.isolateBailoutPropertyAccess);
 | 
| -  }
 | 
| -
 | 
| -  void emitStaticFunctionGetters(StringBuffer buffer) {
 | 
| -    Set<FunctionElement> functionsNeedingGetter =
 | 
| -        compiler.universe.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.isolatePropertyAccess(element);
 | 
| -      int parameterCount = element.parameterCount(compiler);
 | 
| -      String invocationName =
 | 
| -          namer.instanceMethodName(element.getLibrary(), callElement.name,
 | 
| -                                   parameterCount);
 | 
| -      buffer.add("$staticName.$invocationName = $staticName;\n");
 | 
| -      addParameterStubs(callElement, (name) => '$staticName.$name', buffer);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void emitDynamicFunctionGetter(StringBuffer buffer,
 | 
| -                                 String attachTo(String invocationName),
 | 
| -                                 FunctionElement member) {
 | 
| -    // 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):
 | 
| -    //
 | 
| -    // class A {
 | 
| -    //    foo(x, y, z) { ... } // Original function.
 | 
| -    //    get foo() { return new BoundClosure499(this); }
 | 
| -    // }
 | 
| -    // class BoundClosure499 extends Closure {
 | 
| -    //   var self;
 | 
| -    //   BoundClosure499(this.self);
 | 
| -    //   $call3(x, y, z) { return self.foo(x, y, z); }
 | 
| -    // }
 | 
| -
 | 
| -    // TODO(floitsch): share the closure classes with other classes
 | 
| -    // if they share methods with the same signature.
 | 
| -
 | 
| -    // The closure class.
 | 
| -    SourceString name = const SourceString("BoundClosure");
 | 
| -    ClassElement closureClassElement =
 | 
| -        new ClosureClassElement(compiler, member.getCompilationUnit());
 | 
| -    String isolateAccess = namer.isolatePropertyAccess(closureClassElement);
 | 
| -    ensureGenerated(closureClassElement.superclass, buffer);
 | 
| -
 | 
| -    // Define the constructor with a name so that Object.toString can
 | 
| -    // find the class name of the closure class.
 | 
| -    buffer.add("$isolateAccess = function $name(self) ");
 | 
| -    buffer.add("{ this.self = self; };\n");
 | 
| -    emitInherits(closureClassElement, buffer);
 | 
| -
 | 
| -    String prototype = "$isolateAccess.prototype";
 | 
| -
 | 
| -    // 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);
 | 
| -
 | 
| -    int parameterCount = member.parameterCount(compiler);
 | 
| -    String invocationName =
 | 
| -        namer.instanceMethodName(member.getLibrary(),
 | 
| -                                 callElement.name, parameterCount);
 | 
| -    String targetName = namer.instanceMethodName(member.getLibrary(),
 | 
| -                                                 member.name, parameterCount);
 | 
| -    List<String> arguments = new List<String>(parameterCount);
 | 
| -    for (int i = 0; i < parameterCount; i++) {
 | 
| -      arguments[i] = "arg$i";
 | 
| -    }
 | 
| -    String joinedArgs = Strings.join(arguments, ", ");
 | 
| -    buffer.add("$prototype.$invocationName = function($joinedArgs) {\n");
 | 
| -    buffer.add("  return this.self.$targetName($joinedArgs);\n");
 | 
| -    buffer.add("};\n");
 | 
| -    addParameterStubs(callElement,
 | 
| -                      (invocationName) => '$prototype.$invocationName',
 | 
| -                      buffer);
 | 
| -
 | 
| -    // And finally the getter.
 | 
| -    String getterName = namer.getterName(member.getLibrary(), member.name);
 | 
| -    String closureClass = namer.isolateAccess(closureClassElement);
 | 
| -    buffer.add("${attachTo(getterName)} = function() {\n");
 | 
| -    buffer.add("  return new $closureClass(this);\n");
 | 
| -    buffer.add("};\n");
 | 
| -  }
 | 
| -
 | 
| -  void emitCallStubForGetter(StringBuffer buffer,
 | 
| -                             String attachTo(String name),
 | 
| -                             Element member,
 | 
| -                             Set<Selector> selectors) {
 | 
| -    String getter;
 | 
| -    if (member.kind == ElementKind.GETTER) {
 | 
| -      getter = "this.${namer.getterName(member.getLibrary(), member.name)}()";
 | 
| -    } else {
 | 
| -      getter =
 | 
| -          "this.${namer.instanceFieldName(member.getLibrary(), member.name)}";
 | 
| -    }
 | 
| -    for (Selector selector in selectors) {
 | 
| -      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, ", ");
 | 
| -      buffer.add("${attachTo(invocationName)} = function($joined) {\n");
 | 
| -      buffer.add("  return $getter.$closureCallName($joined);\n");
 | 
| -      buffer.add("};\n");
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void emitStaticNonFinalFieldInitializations(StringBuffer buffer) {
 | 
| -    // Adds initializations inside the Isolate constructor.
 | 
| -    // Example:
 | 
| -    //    function Isolate() {
 | 
| -    //       this.staticNonFinal = Isolate.prototype.someVal;
 | 
| -    //       ...
 | 
| -    //    }
 | 
| -    ConstantHandler handler = compiler.constantHandler;
 | 
| -    List<VariableElement> staticNonFinalFields =
 | 
| -        handler.getStaticNonFinalFieldsForEmission();
 | 
| -    if (!staticNonFinalFields.isEmpty()) buffer.add('\n');
 | 
| -    for (Element element in staticNonFinalFields) {
 | 
| -      buffer.add('  this.${namer.getName(element)} = ');
 | 
| -      compiler.withCurrentElement(element, () {
 | 
| -          handler.writeJsCodeForVariable(buffer, element);
 | 
| -        });
 | 
| -      buffer.add(';\n');
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void emitCompileTimeConstants(StringBuffer buffer) {
 | 
| -    ConstantHandler handler = compiler.constantHandler;
 | 
| -    List<Constant> constants = handler.getConstantsForEmission();
 | 
| -    String prototype = "${namer.ISOLATE}.prototype";
 | 
| -    bool addedMakeConstantList = false;
 | 
| -    for (Constant constant in constants) {
 | 
| -      if (!addedMakeConstantList && constant.isList()) {
 | 
| -        addedMakeConstantList = true;
 | 
| -        emitMakeConstantList(prototype, buffer);
 | 
| -      }
 | 
| -      String name = handler.getNameForConstant(constant);
 | 
| -      buffer.add('$prototype.$name = ');
 | 
| -      handler.writeJsCode(buffer, constant);
 | 
| -      buffer.add(';\n');
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void emitMakeConstantList(String prototype, StringBuffer buffer) {
 | 
| -    buffer.add(prototype);
 | 
| -    buffer.add(@'''.makeConstantList = function(list) {
 | 
| -  list.immutable$list = true;
 | 
| -  list.fixed$length = true;
 | 
| -  return list;
 | 
| -};
 | 
| -''');
 | 
| -  }
 | 
| -
 | 
| -  void emitStaticFinalFieldInitializations(StringBuffer buffer) {
 | 
| -    ConstantHandler handler = compiler.constantHandler;
 | 
| -    List<VariableElement> staticFinalFields =
 | 
| -        handler.getStaticFinalFieldsForEmission();
 | 
| -    for (VariableElement element in staticFinalFields) {
 | 
| -      buffer.add('${namer.isolatePropertyAccess(element)} = ');
 | 
| -      compiler.withCurrentElement(element, () {
 | 
| -          handler.writeJsCodeForVariable(buffer, element);
 | 
| -        });
 | 
| -      buffer.add(';\n');
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void emitExtraAccessors(Element member,
 | 
| -                          String attachTo(String name),
 | 
| -                          StringBuffer buffer) {
 | 
| -    if (member.kind == ElementKind.GETTER || member.kind == ElementKind.FIELD) {
 | 
| -      Set<Selector> selectors = compiler.universe.invokedNames[member.name];
 | 
| -      if (selectors !== null && !selectors.isEmpty()) {
 | 
| -        compiler.emitter.emitCallStubForGetter(
 | 
| -            buffer, attachTo, member, selectors);
 | 
| -      }
 | 
| -    } else if (member.kind == ElementKind.FUNCTION) {
 | 
| -      if (compiler.universe.invokedGetters.contains(member.name)) {
 | 
| -        compiler.emitter.emitDynamicFunctionGetter(
 | 
| -            buffer, attachTo, member);
 | 
| -      }
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  void emitNoSuchMethodCalls(StringBuffer buffer) {
 | 
| -    // Do not generate no such method calls if there is no class.
 | 
| -    if (compiler.universe.instantiatedClasses.isEmpty()) return;
 | 
| -
 | 
| -    ClassElement objectClass =
 | 
| -        compiler.coreLibrary.find(const SourceString('Object'));
 | 
| -    String className = namer.isolatePropertyAccess(objectClass);
 | 
| -    String prototype = '$className.prototype';
 | 
| -    String noSuchMethodName =
 | 
| -        namer.instanceMethodName(null, Compiler.NO_SUCH_METHOD, 2);
 | 
| -    Collection<LibraryElement> libraries =
 | 
| -        compiler.universe.libraries.getValues();
 | 
| -
 | 
| -    void generateMethod(String methodName, String jsName, Selector selector) {
 | 
| -      buffer.add('$prototype.$jsName = function');
 | 
| -      StringBuffer args = new StringBuffer();
 | 
| -      for (int i = 0; i < selector.argumentCount; i++) {
 | 
| -        if (i != 0) args.add(', ');
 | 
| -        args.add('arg$i');
 | 
| -      }
 | 
| -      // We need to check if the object has a noSuchMethod. If not, it
 | 
| -      // means the object is a native object, and we can just call our
 | 
| -      // generic noSuchMethod. Note that when calling this method, the
 | 
| -      // 'this' object is not a Dart object.
 | 
| -      buffer.add(' ($args) {\n');
 | 
| -      buffer.add('  return this.$noSuchMethodName\n');
 | 
| -      buffer.add("      ? this.$noSuchMethodName('$methodName', [$args])\n");
 | 
| -      buffer.add("      : $objectClassName.prototype.$noSuchMethodName.call(");
 | 
| -      buffer.add("this, '$methodName', [$args])\n");
 | 
| -      buffer.add('}\n');
 | 
| -    }
 | 
| -
 | 
| -    compiler.universe.invokedNames.forEach((SourceString methodName,
 | 
| -                                            Set<Selector> selectors) {
 | 
| -      if (objectClass.lookupLocalMember(methodName) === null
 | 
| -          && methodName != Namer.OPERATOR_EQUALS) {
 | 
| -        for (Selector selector in selectors) {
 | 
| -          if (methodName.isPrivate()) {
 | 
| -            for (LibraryElement lib in libraries) {
 | 
| -              String jsName =
 | 
| -                namer.instanceMethodInvocationName(lib, methodName, selector);
 | 
| -              generateMethod(methodName.slowToString(), jsName, selector);
 | 
| -            }
 | 
| -          } else {
 | 
| -            String jsName =
 | 
| -              namer.instanceMethodInvocationName(null, methodName, selector);
 | 
| -            generateMethod(methodName.slowToString(), jsName, selector);
 | 
| -          }
 | 
| -        }
 | 
| -      }
 | 
| -    });
 | 
| -
 | 
| -    compiler.universe.invokedGetters.forEach((SourceString getterName) {
 | 
| -      if (getterName.isPrivate()) {
 | 
| -        for (LibraryElement lib in libraries) {
 | 
| -          String jsName = namer.getterName(lib, getterName);
 | 
| -          generateMethod('get ${getterName.slowToString()}', jsName,
 | 
| -                         Selector.GETTER);
 | 
| -        }
 | 
| -      } else {
 | 
| -        String jsName = namer.getterName(null, getterName);
 | 
| -        generateMethod('get ${getterName.slowToString()}', jsName,
 | 
| -                       Selector.GETTER);
 | 
| -      }
 | 
| -    });
 | 
| -
 | 
| -    compiler.universe.invokedSetters.forEach((SourceString setterName) {
 | 
| -      if (setterName.isPrivate()) {
 | 
| -        for (LibraryElement lib in libraries) {
 | 
| -          String jsName = namer.setterName(lib, setterName);
 | 
| -          generateMethod('set ${setterName.slowToString()}', jsName,
 | 
| -                         Selector.SETTER);
 | 
| -        }
 | 
| -      } else {
 | 
| -        String jsName = namer.setterName(null, setterName);
 | 
| -        generateMethod('set ${setterName.slowToString()}', jsName,
 | 
| -                       Selector.SETTER);
 | 
| -      }
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  String buildIsolateSetup(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.universe.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.
 | 
| -    return """
 | 
| -var \$globalThis = $currentIsolate;
 | 
| -var \$globalState;
 | 
| -var \$globals;
 | 
| -function \$static_init(){};
 | 
| -
 | 
| -function \$initGlobals(context) {
 | 
| -  context.isolateStatics = new ${namer.ISOLATE}();
 | 
| -}
 | 
| -function \$setGlobals(context) {
 | 
| -  $currentIsolate = context.isolateStatics;
 | 
| -  \$globalThis = $currentIsolate;
 | 
| -}
 | 
| -$mainEnsureGetter
 | 
| -${namer.isolateAccess(isolateMain)}($mainAccess);""";
 | 
| -  }
 | 
| -
 | 
| -  emitMain(StringBuffer buffer) {
 | 
| -    if (compiler.isMockCompilation) return;
 | 
| -    Element main = compiler.mainApp.find(Compiler.MAIN);
 | 
| -    if (compiler.isolateLibrary != null) {
 | 
| -      Element isolateMain =
 | 
| -        compiler.isolateLibrary.find(Compiler.START_ROOT_ISOLATE);
 | 
| -      buffer.add(buildIsolateSetup(main, isolateMain));
 | 
| -    } else {
 | 
| -      buffer.add('${namer.isolateAccess(main)}();\n');
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  String assembleProgram() {
 | 
| -    measure(() {
 | 
| -      mainBuffer.add('function ${namer.ISOLATE}() {');
 | 
| -      emitStaticNonFinalFieldInitializations(mainBuffer);
 | 
| -      mainBuffer.add('}\n\n');
 | 
| -      emitClasses(mainBuffer);
 | 
| -      emitStaticFunctions(mainBuffer);
 | 
| -      emitStaticFunctionGetters(mainBuffer);
 | 
| -      emitCompileTimeConstants(mainBuffer);
 | 
| -      emitStaticFinalFieldInitializations(mainBuffer);
 | 
| -      nativeEmitter.emitDynamicDispatchMetadata();
 | 
| -      mainBuffer.add(
 | 
| -          'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n');
 | 
| -      nativeEmitter.assembleCode(mainBuffer);
 | 
| -      emitMain(mainBuffer);
 | 
| -      compiler.assembledCode = mainBuffer.toString();
 | 
| -    });
 | 
| -    return compiler.assembledCode;
 | 
| -  }
 | 
| -}
 | 
| 
 |