blob: 6e37f27edaf5e9bce1be9c4199c4851c21c1f9f6 [file] [log] [blame]
// Copyright (c) 2014, 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.
library dart2js.new_js_emitter.model_emitter;
import '../../dart2jslib.dart' show Compiler;
import '../../dart_types.dart' show DartType;
import '../../elements/elements.dart' show ClassElement;
import '../../js/js.dart' as js;
import '../../js_backend/js_backend.dart' show
JavaScriptBackend,
Namer,
ConstantEmitter;
import '../js_emitter.dart' show
NativeEmitter;
import 'package:_internal/compiler/js_lib/shared/embedded_names.dart' show
DEFERRED_LIBRARY_URIS,
DEFERRED_LIBRARY_HASHES,
GET_TYPE_FROM_NAME,
INITIALIZE_LOADED_HUNK,
INTERCEPTORS_BY_TAG,
IS_HUNK_INITIALIZED,
IS_HUNK_LOADED,
LEAF_TAGS,
MANGLED_GLOBAL_NAMES,
METADATA,
TYPE_TO_INTERCEPTOR_MAP;
import '../js_emitter.dart' show NativeGenerator, buildTearOffCode;
import '../model.dart';
class ModelEmitter {
final Compiler compiler;
final Namer namer;
final ConstantEmitter constantEmitter;
final NativeEmitter nativeEmitter;
JavaScriptBackend get backend => compiler.backend;
/// For deferred loading we communicate the initializers via this global var.
static const String deferredInitializersGlobal =
r"$__dart_deferred_initializers__";
static const String deferredExtension = "part.js";
ModelEmitter(Compiler compiler, Namer namer, this.nativeEmitter)
: this.compiler = compiler,
this.namer = namer,
constantEmitter =
new ConstantEmitter(compiler, namer, makeConstantListTemplate);
js.Expression generateEmbeddedGlobalAccess(String global) {
// TODO(floitsch): We should not use "init" for globals.
return js.js("init.$global");
}
int emitProgram(Program program) {
List<Fragment> fragments = program.fragments;
MainFragment mainFragment = fragments.first;
js.Statement mainAst = emitMainFragment(program);
String mainCode = js.prettyPrint(mainAst, compiler).getText();
compiler.outputProvider(mainFragment.outputFileName, 'js')
..add(buildGeneratedBy(compiler))
..add(mainCode)
..close();
int totalSize = mainCode.length;
fragments.skip(1).forEach((DeferredFragment deferredUnit) {
js.Expression ast =
emitDeferredFragment(deferredUnit, mainFragment.holders);
String code = js.prettyPrint(ast, compiler).getText();
totalSize += code.length;
compiler.outputProvider(deferredUnit.outputFileName, deferredExtension)
..add(code)
..close();
});
return totalSize;
}
js.LiteralString unparse(Compiler compiler, js.Expression value) {
String text = js.prettyPrint(value, compiler).getText();
if (value is js.Fun) text = '($text)';
return js.js.escapedString(text);
}
String buildGeneratedBy(compiler) {
var suffix = '';
if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n';
}
js.Statement emitMainFragment(Program program) {
MainFragment fragment = program.fragments.first;
List<js.Expression> elements = fragment.libraries.map(emitLibrary).toList();
elements.add(
emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields));
js.Expression code = new js.ArrayInitializer(elements);
Map<String, dynamic> holes =
{'deferredInitializer': emitDeferredInitializerGlobal(program.loadMap),
'holders': emitHolders(fragment.holders),
'tearOff': buildTearOffCode(backend),
'parseFunctionDescriptor':
js.js.statement(parseFunctionDescriptorBoilerplate),
'cyclicThrow':
backend.emitter.staticFunctionAccess(backend.getCyclicThrowHelper()),
'outputContainsConstantList': program.outputContainsConstantList,
'embeddedGlobals': emitEmbeddedGlobals(program),
'constants': emitConstants(fragment.constants),
'staticNonFinals':
emitStaticNonFinalFields(fragment.staticNonFinalFields),
'operatorIsPrefix': js.string(namer.operatorIsPrefix),
'eagerClasses': emitEagerClassInitializations(fragment.libraries),
'main': fragment.main,
'code': code};
holes.addAll(nativeHoles(program));
return js.js.statement(boilerplate, holes);
}
Map<String, dynamic> nativeHoles(Program program) {
Map<String, dynamic> nativeHoles = <String, dynamic>{};
js.Statement nativeIsolateAffinityTagInitialization;
if (NativeGenerator.needsIsolateAffinityTagInitialization(backend)) {
nativeIsolateAffinityTagInitialization =
NativeGenerator.generateIsolateAffinityTagInitialization(
backend,
generateEmbeddedGlobalAccess,
// TODO(floitsch): convertToFastObject.
js.js("(function(x) { return x; })", []));
} else {
nativeIsolateAffinityTagInitialization = js.js.statement(";");
}
nativeHoles['nativeIsolateAffinityTagInitialization'] =
nativeIsolateAffinityTagInitialization;
js.Expression nativeInfoAccess = js.js('nativeInfo', []);
js.Expression constructorAccess = js.js('constructor', []);
Function subclassReadGenerator = (js.Expression subclass) {
return js.js('holdersMap[#][#].ensureResolved()', [subclass, subclass]);
};
js.Expression interceptorsByTagAccess =
generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG);
js.Expression leafTagsAccess =
generateEmbeddedGlobalAccess(LEAF_TAGS);
js.Statement nativeInfoHandler = nativeEmitter.buildNativeInfoHandler(
nativeInfoAccess,
constructorAccess,
subclassReadGenerator,
interceptorsByTagAccess,
leafTagsAccess);
nativeHoles['hasNativeClasses'] = program.outputContainsNativeClasses;
nativeHoles['hasNoNativeClasses'] = !program.outputContainsNativeClasses;
nativeHoles['nativeInfoHandler'] = nativeInfoHandler;
return nativeHoles;
}
js.Block emitHolders(List<Holder> holders) {
// The top-level variables for holders must *not* be renamed by the
// JavaScript pretty printer because a lot of code already uses the
// non-renamed names. The generated code looks like this:
//
// var H = {}, ..., G = {};
// var holders = [ H, ..., G ];
//
// and it is inserted at the top of the top-level function expression
// that covers the entire program.
List<js.Statement> statements = [
new js.ExpressionStatement(
new js.VariableDeclarationList(holders.map((e) =>
new js.VariableInitialization(
new js.VariableDeclaration(e.name, allowRename: false),
new js.ObjectInitializer(const []))).toList())),
js.js.statement('var holders = #', new js.ArrayInitializer(
holders.map((e) => new js.VariableUse(e.name))
.toList(growable: false))),
js.js.statement('var holdersMap = Object.create(null)')
];
return new js.Block(statements);
}
static js.Template get makeConstantListTemplate {
// TODO(floitsch): remove hard-coded name.
// TODO(floitsch): there is no harm in caching the template.
return js.js.uncachedExpressionTemplate('makeConstList(#)');
}
js.Block emitEmbeddedGlobals(Program program) {
List<js.Property> globals = <js.Property>[];
if (program.loadMap.isNotEmpty) {
globals.addAll(emitLoadUrisAndHashes(program.loadMap));
globals.add(emitIsHunkLoadedFunction());
globals.add(emitInitializeLoadedHunk());
}
if (program.typeToInterceptorMap != null) {
globals.add(new js.Property(js.string(TYPE_TO_INTERCEPTOR_MAP),
program.typeToInterceptorMap));
}
globals.add(emitMangledGlobalNames());
globals.add(emitGetTypeFromName());
globals.add(emitMetadata(program));
if (program.outputContainsNativeClasses) {
globals.add(new js.Property(js.string(INTERCEPTORS_BY_TAG),
js.js('Object.create(null)', [])));
globals.add(new js.Property(js.string(LEAF_TAGS),
js.js('Object.create(null)', [])));
}
js.ObjectInitializer globalsObject = new js.ObjectInitializer(globals);
List<js.Statement> statements =
[new js.ExpressionStatement(
new js.VariableDeclarationList(
[new js.VariableInitialization(
new js.VariableDeclaration("init", allowRename: false),
globalsObject)]))];
return new js.Block(statements);
}
js.Property emitMangledGlobalNames() {
List<js.Property> names = <js.Property>[];
// We want to keep the original names for the most common core classes when
// calling toString on them.
List<ClassElement> nativeClassesNeedingUnmangledName =
[compiler.intClass, compiler.doubleClass, compiler.numClass,
compiler.stringClass, compiler.boolClass, compiler.nullClass,
compiler.listClass];
nativeClassesNeedingUnmangledName.forEach((element) {
names.add(new js.Property(js.string(namer.getNameOfClass(element)),
js.string(element.name)));
});
return new js.Property(js.string(MANGLED_GLOBAL_NAMES),
new js.ObjectInitializer(names));
}
List<js.Property> emitLoadUrisAndHashes(Map<String, List<Fragment>> loadMap) {
js.ArrayInitializer outputUris(List<Fragment> fragments) {
return js.stringArray(fragments.map((DeferredFragment fragment) =>
"${fragment.outputFileName}$deferredExtension"));
}
js.ArrayInitializer outputHashes(List<Fragment> fragments) {
// TODO(floitsch): the hash must depend on the generated code.
return js.numArray(
fragments.map((DeferredFragment fragment) => fragment.hashCode));
}
List<js.Property> uris = new List<js.Property>(loadMap.length);
List<js.Property> hashes = new List<js.Property>(loadMap.length);
int count = 0;
loadMap.forEach((String loadId, List<Fragment> fragmentList) {
uris[count] =
new js.Property(js.string(loadId), outputUris(fragmentList));
hashes[count] =
new js.Property(js.string(loadId), outputHashes(fragmentList));
count++;
});
return <js.Property>[
new js.Property(js.string(DEFERRED_LIBRARY_URIS),
new js.ObjectInitializer(uris)),
new js.Property(js.string(DEFERRED_LIBRARY_HASHES),
new js.ObjectInitializer(hashes))
];
}
js.Statement emitDeferredInitializerGlobal(Map loadMap) {
if (loadMap.isEmpty) return new js.Block.empty();
return js.js.statement("""
if (typeof($deferredInitializersGlobal) === 'undefined')
var $deferredInitializersGlobal = Object.create(null);""");
}
js.Property emitIsHunkLoadedFunction() {
js.Expression function =
js.js("function(hash) { return !!$deferredInitializersGlobal[hash]; }");
return new js.Property(js.string(IS_HUNK_LOADED), function);
}
js.Property emitInitializeLoadedHunk() {
js.Expression function =
js.js("function(hash) { eval($deferredInitializersGlobal[hash]); }");
return new js.Property(js.string(INITIALIZE_LOADED_HUNK), function);
}
js.Property emitGetTypeFromName() {
js.Expression function =
js.js( """function(name) {
return holdersMap[name][name].ensureResolved();
}""");
return new js.Property(js.string(GET_TYPE_FROM_NAME), function);
}
js.Property emitMetadata(Program program) {
String metadataList = "[${program.metadata.join(",")}]";
js.Expression metadata =
js.js.uncachedExpressionTemplate(metadataList).instantiate([]);
return new js.Property(js.string(METADATA), metadata);
}
js.Expression emitDeferredFragment(DeferredFragment fragment,
List<Holder> holders) {
// TODO(floitsch): initialize eager classes.
// TODO(floitsch): the hash must depend on the output.
int hash = this.hashCode;
if (fragment.constants.isNotEmpty) {
throw new UnimplementedError("constants in deferred units");
}
js.ArrayInitializer content =
new js.ArrayInitializer(fragment.libraries.map(emitLibrary)
.toList(growable: false));
return js.js("$deferredInitializersGlobal[$hash] = #", content);
}
js.Block emitConstants(List<Constant> constants) {
Iterable<js.Statement> statements = constants.map((Constant constant) {
js.Expression code =
constantEmitter.initializationExpression(constant.value);
return js.js.statement("#.# = #;",
[constant.holder.name, constant.name, code]);
});
return new js.Block(statements.toList());
}
js.Block emitStaticNonFinalFields(List<StaticField> fields) {
Iterable<js.Statement> statements = fields.map((StaticField field) {
return js.js.statement("#.# = #;",
[field.holder.name, field.name, field.code]);
});
return new js.Block(statements.toList());
}
js.Expression emitLazilyInitializedStatics(List<StaticField> fields) {
Iterable fieldDescriptors = fields.expand((field) =>
[ js.string(field.name),
js.string("${namer.getterPrefix}${field.name}"),
js.number(field.holder.index),
emitLazyInitializer(field) ]);
return new js.ArrayInitializer(fieldDescriptors.toList(growable: false));
}
js.Block emitEagerClassInitializations(List<Library> libraries) {
js.Statement createInstantiation(Class cls) {
return js.js.statement('new #.#()', [cls.holder.name, cls.name]);
}
List<js.Statement> instantiations =
libraries.expand((Library library) => library.classes)
.where((Class cls) => cls.isEager)
.map(createInstantiation)
.toList(growable: false);
return new js.Block(instantiations);
}
// This string should be referenced wherever JavaScript code makes assumptions
// on the mixin format.
static final String nativeInfoDescription =
"A class is encoded as follows:"
" [name, class-code, holder-index], or "
" [name, class-code, native-info, holder-index].";
js.Expression emitLibrary(Library library) {
Iterable staticDescriptors = library.statics.expand(emitStaticMethod);
Iterable classDescriptors = library.classes.expand((Class cls) {
js.LiteralString name = js.string(cls.name);
js.LiteralNumber holderIndex = js.number(cls.holder.index);
js.Expression emittedClass = emitClass(cls);
if (cls.nativeInfo == null) {
return [name, emittedClass, holderIndex];
} else {
return [name, emittedClass, js.string(cls.nativeInfo), holderIndex];
}
});
js.Expression staticArray =
new js.ArrayInitializer(staticDescriptors.toList(growable: false));
js.Expression classArray =
new js.ArrayInitializer(classDescriptors.toList(growable: false));
return new js.ArrayInitializer([staticArray, classArray]);
}
js.Expression _generateConstructor(Class cls) {
List<String> fieldNames = <String>[];
// If the class is not directly instantiated we only need it for inheritance
// or RTI. In either case we don't need its fields.
if (cls.isDirectlyInstantiated && !cls.isNative) {
fieldNames = cls.fields.map((Field field) => field.name).toList();
}
String name = cls.name;
String parameters = fieldNames.join(', ');
String assignments = fieldNames
.map((String field) => "this.$field = $field;\n")
.join();
String code = 'function $name($parameters) { $assignments }';
js.Template template = js.js.uncachedExpressionTemplate(code);
return template.instantiate(const []);
}
Method _generateGetter(Field field) {
String getterTemplateFor(int flags) {
switch (flags) {
case 1: return "function() { return this[#]; }";
case 2: return "function(receiver) { return receiver[#]; }";
case 3: return "function(receiver) { return this[#]; }";
}
return null;
}
js.Expression fieldName = js.string(field.name);
js.Expression code = js.js(getterTemplateFor(field.getterFlags), fieldName);
String getterName = "${namer.getterPrefix}${field.accessorName}";
return new StubMethod(getterName, code);
}
Method _generateSetter(Field field) {
String setterTemplateFor(int flags) {
switch (flags) {
case 1: return "function(val) { return this[#] = val; }";
case 2: return "function(receiver, val) { return receiver[#] = val; }";
case 3: return "function(receiver, val) { return this[#] = val; }";
}
return null;
}
js.Expression fieldName = js.string(field.name);
js.Expression code = js.js(setterTemplateFor(field.setterFlags), fieldName);
String setterName = "${namer.setterPrefix}${field.accessorName}";
return new StubMethod(setterName, code);
}
Iterable<Method> _generateGettersSetters(Class cls) {
Iterable<Method> getters = cls.fields
.where((Field field) => field.needsGetter)
.map(_generateGetter);
Iterable<Method> setters = cls.fields
.where((Field field) => field.needsUncheckedSetter)
.map(_generateSetter);
return [getters, setters].expand((x) => x);
}
// This string should be referenced wherever JavaScript code makes assumptions
// on the mixin format.
static final String mixinFormatDescription =
"Mixins have no constructor, but a reference to their mixin class.";
js.Expression emitClass(Class cls) {
List elements = [js.string(cls.superclassName),
js.number(cls.superclassHolderIndex)];
if (cls.isMixinApplication) {
MixinApplication mixin = cls;
elements.add(js.string(mixin.mixinClass.name));
elements.add(js.number(mixin.mixinClass.holder.index));
} else {
elements.add(_generateConstructor(cls));
}
Iterable<Method> methods = cls.methods;
Iterable<Method> isChecks = cls.isChecks;
Iterable<Method> callStubs = cls.callStubs;
Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs;
Iterable<Method> gettersSetters = _generateGettersSetters(cls);
Iterable<Method> allMethods =
[methods, isChecks, callStubs, noSuchMethodStubs, gettersSetters]
.expand((x) => x);
elements.addAll(allMethods.expand(emitInstanceMethod));
return unparse(compiler, new js.ArrayInitializer(elements));
}
js.Expression emitLazyInitializer(StaticField field) {
assert(field.isLazy);
return unparse(compiler, field.code);
}
/// JavaScript code template that implements parsing of a function descriptor.
/// Descriptors are used in place of the actual JavaScript function
/// definition in the output if additional information needs to be passed to
/// facilitate the generation of tearOffs at runtime. The format is an array
/// with the following fields:
///
/// [Method.code]
/// [DartMethod.callName]
/// [DartMethod.tearOffName]
/// [JavaScriptBackend.isInterceptedMethod]
/// functionType
/// [InstanceMethod.aliasName]
///
/// followed by
///
/// [ParameterStubMethod.name]
/// [ParameterStubMethod.code]
///
/// for each stub in [DartMethod.parameterStubs].
static final String parseFunctionDescriptorBoilerplate = r"""
function parseFunctionDescriptor(proto, name, descriptor) {
if (descriptor instanceof Array) {
proto[name] = descriptor[0];
var funs = [descriptor[0]];
funs[0].$callName = descriptor[1];
for (var pos = 6; pos < descriptor.length; pos += 3) {
var stub = descriptor[pos + 2];
stub.$callName = descriptor[pos + 1];
proto[descriptor[pos]] = stub;
funs.push(stub);
}
if (descriptor[2] != null) {
var isIntercepted = descriptor[3];
var reflectionInfo = descriptor[4];
proto[descriptor[2]] =
tearOff(funs, reflectionInfo, false, name, isIntercepted);
}
// Install the alias for super calls on the prototype chain.
if (descriptor[5] != null) {
proto[descriptor[5]] = descriptor[0];
}
} else {
proto[name] = descriptor;
}
}
""";
js.Expression _generateFunctionType(DartType memberType) {
if (memberType.containsTypeVariables) {
js.Expression thisAccess = js.js(r'this.$receiver');
return backend.rti.getSignatureEncoding(memberType, thisAccess);
} else {
return js.number(backend.emitter.metadataCollector.reifyType(memberType));
}
}
Iterable<js.Expression> emitInstanceMethod(Method method) {
List<js.Expression> makeNameCodePair(Method method) {
return [js.string(method.name), method.code];
}
List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) {
js.Expression callName = stub.callName == null
? new js.LiteralNull()
: js.string(stub.callName);
return [js.string(stub.name), callName, stub.code];
}
if (method is InstanceMethod) {
if (method.needsTearOff || method.aliasName != null) {
/// See [parseFunctionDescriptorBoilerplate] for a full description of
/// the format.
// [name, [function, callName, tearOffName, isIntercepted, functionType,
// aliasName, stub1_name, stub1_callName, stub1_code, ...]
bool isIntercepted = backend.isInterceptedMethod(method.element);
var data = [method.code];
data.add(js.string(method.callName));
data.add(js.string(method.tearOffName));
data.add(new js.LiteralBool(isIntercepted));
data.add(_generateFunctionType(method.type));
if (method.aliasName != null) {
data.add(js.string(method.aliasName));
} else {
data.add(new js.LiteralNull());
}
data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet));
return [js.string(method.name), new js.ArrayInitializer(data)];
} else {
// TODO(floitsch): not the most efficient way...
return ([method]..addAll(method.parameterStubs))
.expand(makeNameCodePair);
}
} else {
return makeNameCodePair(method);
}
}
Iterable<js.Expression> emitStaticMethod(StaticMethod method) {
js.Expression holderIndex = js.number(method.holder.index);
List<js.Expression> output = <js.Expression>[];
void _addMethod(Method method) {
js.Expression unparsed = unparse(compiler, method.code);
output.add(js.string(method.name));
output.add(holderIndex);
output.add(unparsed);
}
List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) {
js.Expression callName = stub.callName == null
? new js.LiteralNull()
: js.string(stub.callName);
return [js.string(stub.name), callName, unparse(compiler, stub.code)];
}
_addMethod(method);
// TODO(floitsch): can there be anything else than a StaticDartMethod?
if (method is StaticDartMethod) {
if (method.needsTearOff) {
/// The format emitted is the same as for the parser specified at
/// [parseFunctionDescriptorBoilerplate] except for the missing
/// field whether the method is intercepted.
// [name, [function, callName, tearOffName, functionType,
// stub1_name, stub1_callName, stub1_code, ...]
var data = [unparse(compiler, method.code)];
data.add(js.string(method.callName));
data.add(js.string(method.tearOffName));
data.add(_generateFunctionType(method.type));
data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet));
return [js.string(method.name), holderIndex,
new js.ArrayInitializer(data)];
} else {
method.parameterStubs.forEach(_addMethod);
}
}
return output;
}
static final String boilerplate = """
{
// Declare deferred-initializer global.
#deferredInitializer;
!function(start, program) {
// Initialize holder objects.
#holders;
var nativeInfos = Object.create(null);
// Counter to generate unique names for tear offs.
var functionCounter = 0;
function setupProgram() {
for (var i = 0; i < program.length - 1; i++) {
setupLibrary(program[i]);
}
setupLazyStatics(program[i]);
}
function setupLibrary(library) {
var statics = library[0];
for (var i = 0; i < statics.length; i += 3) {
var holderIndex = statics[i + 1];
setupStatic(statics[i], holders[holderIndex], statics[i + 2]);
}
var classes = library[1];
for (var i = 0; i < classes.length; i += 3) {
var name = classes[i];
var cls = classes[i + 1];
if (#hasNativeClasses) {
// $nativeInfoDescription.
var indexOrNativeInfo = classes[i + 2];
if (typeof indexOrNativeInfo == "number") {
var holderIndex = classes[i + 2];
} else {
nativeInfos[name] = indexOrNativeInfo;
holderIndex = classes[i + 3];
i++;
}
}
if (#hasNoNativeClasses) {
var holderIndex = classes[i + 2];
}
holdersMap[name] = holders[holderIndex];
setupClass(name, holders[holderIndex], cls);
}
}
function setupLazyStatics(statics) {
for (var i = 0; i < statics.length; i += 4) {
var name = statics[i];
var getterName = statics[i + 1];
var holderIndex = statics[i + 2];
var initializer = statics[i + 3];
setupLazyStatic(name, getterName, holders[holderIndex], initializer);
}
}
function setupStatic(name, holder, descriptor) {
if (typeof descriptor == 'string') {
holder[name] = function() {
var method = compile(name, descriptor);
holder[name] = method;
return method.apply(this, arguments);
};
} else {
// Parse the tear off information and generate compile handlers.
// TODO(herhut): Share parser with instance methods.
function compileAllStubs() {
var funs;
var fun = compile(name, descriptor[0]);
fun.\$callName = descriptor[1];
holder[name] = fun;
funs = [fun];
for (var pos = 4; pos < descriptor.length; pos += 3) {
var stubName = descriptor[pos];
fun = compile(stubName, descriptor[pos + 2]);
fun.\$callName = descriptor[pos + 1];
holder[stubName] = fun;
funs.push(fun);
}
if (descriptor[2] != null) { // tear-off name.
// functions, reflectionInfo, isStatic, name, isIntercepted.
holder[descriptor[2]] =
tearOff(funs, descriptor[3], true, name, false);
}
}
function setupCompileAllAndDelegateStub(name) {
holder[name] = function() {
compileAllStubs();
return holder[name].apply(this, arguments);
};
}
setupCompileAllAndDelegateStub(name);
for (var pos = 4; pos < descriptor.length; pos += 3) {
setupCompileAllAndDelegateStub(descriptor[pos]);
}
if (descriptor[2] != null) { // tear-off name.
setupCompileAllAndDelegateStub(descriptor[2])
}
}
}
function setupLazyStatic(name, getterName, holder, descriptor) {
holder[name] = null;
holder[getterName] = function() {
var initializer = compile(name, descriptor);
holder[getterName] = function() { #cyclicThrow(name) };
var result;
var sentinelInProgress = descriptor;
try {
result = holder[name] = sentinelInProgress;
result = holder[name] = initializer();
} finally {
// Use try-finally, not try-catch/throw as it destroys the stack trace.
if (result === sentinelInProgress) {
// The lazy static (holder[name]) might have been set to a different
// value. According to spec we still have to reset it to null, if the
// initialization failed.
holder[name] = null;
}
holder[getterName] = function() { return this[name]; };
}
return result;
};
}
function setupClass(name, holder, descriptor) {
var ensureResolved = function() {
var constructor = compileConstructor(name, descriptor);
holder[name] = constructor;
constructor.ensureResolved = function() { return this; };
return constructor;
};
var patch = function() {
var constructor = ensureResolved();
var object = new constructor();
constructor.apply(object, arguments);
return object;
};
// We store the ensureResolved function on the patch function to make it
// possible to resolve superclass references without constructing instances.
patch.ensureResolved = ensureResolved;
holder[name] = patch;
}
#tearOff;
#parseFunctionDescriptor;
function compileConstructor(name, descriptor) {
descriptor = compile(name, descriptor);
var prototype = determinePrototype(descriptor);
var constructor;
// $mixinFormatDescription.
if (typeof descriptor[2] !== 'function') {
constructor = compileMixinConstructor(name, prototype, descriptor);
for (var i = 4; i < descriptor.length; i += 2) {
parseFunctionDescriptor(prototype, descriptor[i], descriptor[i + 1]);
}
} else {
constructor = descriptor[2];
for (var i = 3; i < descriptor.length; i += 2) {
parseFunctionDescriptor(prototype, descriptor[i], descriptor[i + 1]);
}
}
constructor.builtin\$cls = name; // Needed for RTI.
constructor.prototype = prototype;
prototype[#operatorIsPrefix + name] = constructor;
prototype.constructor = constructor;
return constructor;
}
function compileMixinConstructor(name, prototype, descriptor) {
// $mixinFormatDescription.
var mixinName = descriptor[2];
var mixinHolderIndex = descriptor[3];
var mixin = holders[mixinHolderIndex][mixinName].ensureResolved();
var mixinPrototype = mixin.prototype;
// Fill the prototype with the mixin's properties.
var mixinProperties = Object.keys(mixinPrototype);
for (var i = 0; i < mixinProperties.length; i++) {
var p = mixinProperties[i];
prototype[p] = mixinPrototype[p];
}
// Since this is a mixin application the constructor will actually never
// be invoked. We only use its prototype for the application's subclasses.
var constructor = function() {};
return constructor;
}
function determinePrototype(descriptor) {
var superclassName = descriptor[0];
if (!superclassName) return { };
// Look up the superclass constructor function in the right holder.
var holderIndex = descriptor[1];
var superclass = holders[holderIndex][superclassName].ensureResolved();
// Create a new prototype object chained to the superclass prototype.
var intermediate = function() { };
intermediate.prototype = superclass.prototype;
return new intermediate();
}
function compile(__name__, __s__) {
'use strict';
// TODO(floitsch): evaluate the performance impact of the string
// concatenations.
return eval(__s__ + "\\n//# sourceURL=" + __name__ + ".js");
}
if (#outputContainsConstantList) {
function makeConstList(list) {
// By assigning a function to the properties they become part of the
// hidden class. The actual values of the fields don't matter, since we
// only check if they exist.
list.immutable\$list = Array;
list.fixed\$length = Array;
return list;
}
}
if (#hasNativeClasses) {
function handleNativeClassInfos() {
for (var nativeClass in nativeInfos) {
var constructor = holdersMap[nativeClass][nativeClass].ensureResolved();
var nativeInfo = nativeInfos[nativeClass];
#nativeInfoHandler;
}
}
}
setupProgram();
// Initialize constants.
#constants;
// Initialize globals.
#embeddedGlobals;
// TODO(floitsch): this order means that native classes may not be
// referenced from constants. I'm mostly afraid of things like using them as
// generic arguments (which should be fine, but maybe there are other
// similar things).
// Initialize natives.
if (#hasNativeClasses) handleNativeClassInfos();
// Initialize static non-final fields.
#staticNonFinals;
// Add native boilerplate code.
#nativeIsolateAffinityTagInitialization;
// Initialize eager classes.
#eagerClasses;
var end = Date.now();
print('Setup: ' + (end - start) + ' ms.');
#main(); // Start main.
}(Date.now(), #code)
}""";
}