blob: d47144373d4d1a98826329b4f700a438f66e79fc [file] [log] [blame]
// 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;
}
}