| Index: sdk/lib/_internal/compiler/implementation/js_emitter/old_emitter/emitter.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/old_emitter/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/old_emitter/emitter.dart
|
| deleted file mode 100644
|
| index 6c266153502120b97a67edcb207ad9877eaddfc2..0000000000000000000000000000000000000000
|
| --- a/sdk/lib/_internal/compiler/implementation/js_emitter/old_emitter/emitter.dart
|
| +++ /dev/null
|
| @@ -1,1998 +0,0 @@
|
| -// 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.
|
| -
|
| -part of dart2js.js_emitter;
|
| -
|
| -
|
| -class OldEmitter implements Emitter {
|
| - final Compiler compiler;
|
| - final CodeEmitterTask task;
|
| -
|
| - final ContainerBuilder containerBuilder = new ContainerBuilder();
|
| - final ClassEmitter classEmitter = new ClassEmitter();
|
| - final NsmEmitter nsmEmitter = new NsmEmitter();
|
| - TypeTestEmitter get typeTestEmitter => task.typeTestEmitter;
|
| - final InterceptorEmitter interceptorEmitter = new InterceptorEmitter();
|
| - final MetadataEmitter metadataEmitter = new MetadataEmitter();
|
| -
|
| - final Set<ConstantValue> cachedEmittedConstants;
|
| - final CodeBuffer cachedEmittedConstantsBuffer = new CodeBuffer();
|
| - final Map<Element, ClassBuilder> cachedClassBuilders;
|
| - final Set<Element> cachedElements;
|
| -
|
| - bool needsDefineClass = false;
|
| - bool needsMixinSupport = false;
|
| - bool needsLazyInitializer = false;
|
| - final Namer namer;
|
| - ConstantEmitter constantEmitter;
|
| - NativeEmitter get nativeEmitter => task.nativeEmitter;
|
| -
|
| - // The full code that is written to each hunk part-file.
|
| - Map<OutputUnit, CodeBuffer> outputBuffers = new Map<OutputUnit, CodeBuffer>();
|
| - final CodeBuffer deferredConstants = new CodeBuffer();
|
| -
|
| - /** Shorter access to [isolatePropertiesName]. Both here in the code, as
|
| - well as in the generated code. */
|
| - String isolateProperties;
|
| - String classesCollector;
|
| - Set<ClassElement> get neededClasses => task.neededClasses;
|
| - Map<OutputUnit, List<ClassElement>> get outputClassLists
|
| - => task.outputClassLists;
|
| - Map<OutputUnit, List<ConstantValue>> get outputConstantLists
|
| - => task.outputConstantLists;
|
| - List<ClassElement> get nativeClasses => task.nativeClasses;
|
| - final Map<String, String> mangledFieldNames = <String, String>{};
|
| - final Map<String, String> mangledGlobalFieldNames = <String, String>{};
|
| - final Set<String> recordedMangledNames = new Set<String>();
|
| -
|
| - final Map<ClassElement, Map<String, jsAst.Expression>> additionalProperties =
|
| - new Map<ClassElement, Map<String, jsAst.Expression>>();
|
| -
|
| - List<TypedefElement> get typedefsNeededForReflection =>
|
| - task.typedefsNeededForReflection;
|
| -
|
| - JavaScriptBackend get backend => compiler.backend;
|
| - TypeVariableHandler get typeVariableHandler => backend.typeVariableHandler;
|
| -
|
| - String get _ => space;
|
| - String get space => compiler.enableMinification ? "" : " ";
|
| - String get n => compiler.enableMinification ? "" : "\n";
|
| - String get N => compiler.enableMinification ? "\n" : ";\n";
|
| -
|
| - CodeBuffer getBuffer(OutputUnit outputUnit) {
|
| - return outputBuffers.putIfAbsent(outputUnit, () => new CodeBuffer());
|
| - }
|
| -
|
| - CodeBuffer get mainBuffer {
|
| - return getBuffer(compiler.deferredLoadTask.mainOutputUnit);
|
| - }
|
| -
|
| - /**
|
| - * List of expressions and statements that will be included in the
|
| - * precompiled function.
|
| - *
|
| - * To save space, dart2js normally generates constructors and accessors
|
| - * dynamically. This doesn't work in CSP mode, and may impact startup time
|
| - * negatively. So dart2js will emit these functions to a separate file that
|
| - * can be optionally included to support CSP mode or for faster startup.
|
| - */
|
| - Map<OutputUnit, List<jsAst.Node>> _cspPrecompiledFunctions =
|
| - new Map<OutputUnit, List<jsAst.Node>>();
|
| -
|
| - Map<OutputUnit, List<jsAst.Expression>> _cspPrecompiledConstructorNames =
|
| - new Map<OutputUnit, List<jsAst.Expression>>();
|
| -
|
| - /**
|
| - * Accumulate properties for classes and libraries, describing their
|
| - * static/top-level members.
|
| - * Later, these members are emitted when the class or library is emitted.
|
| - *
|
| - * See [getElementDescriptor].
|
| - */
|
| - // TODO(ahe): Generate statics with their class, and store only libraries in
|
| - // this map.
|
| - final Map<Element, ClassBuilder> elementDescriptors =
|
| - new Map<Element, ClassBuilder>();
|
| -
|
| - final bool generateSourceMap;
|
| -
|
| - OldEmitter(Compiler compiler, Namer namer, this.generateSourceMap, this.task)
|
| - : this.compiler = compiler,
|
| - this.namer = namer,
|
| - cachedEmittedConstants = compiler.cacheStrategy.newSet(),
|
| - cachedClassBuilders = compiler.cacheStrategy.newMap(),
|
| - cachedElements = compiler.cacheStrategy.newSet() {
|
| - constantEmitter =
|
| - new ConstantEmitter(compiler, namer, makeConstantListTemplate);
|
| - containerBuilder.emitter = this;
|
| - classEmitter.emitter = this;
|
| - nsmEmitter.emitter = this;
|
| - interceptorEmitter.emitter = this;
|
| - metadataEmitter.emitter = this;
|
| - }
|
| -
|
| - List<jsAst.Node> cspPrecompiledFunctionFor(OutputUnit outputUnit) {
|
| - return _cspPrecompiledFunctions.putIfAbsent(
|
| - outputUnit,
|
| - () => new List<jsAst.Node>());
|
| - }
|
| -
|
| - List<jsAst.Expression> cspPrecompiledConstructorNamesFor(
|
| - OutputUnit outputUnit) {
|
| - return _cspPrecompiledConstructorNames.putIfAbsent(
|
| - outputUnit,
|
| - () => new List<jsAst.Expression>());
|
| - }
|
| -
|
| - /// Erases the precompiled information for csp mode for all output units.
|
| - /// Used by the incremental compiler.
|
| - void clearCspPrecompiledNodes() {
|
| - _cspPrecompiledFunctions.clear();
|
| - _cspPrecompiledConstructorNames.clear();
|
| - }
|
| -
|
| - void addComment(String comment, CodeBuffer buffer) {
|
| - buffer.write(jsAst.prettyPrint(js.comment(comment), compiler));
|
| - }
|
| -
|
| - jsAst.Expression constantReference(ConstantValue value) {
|
| - return constantEmitter.reference(value);
|
| - }
|
| -
|
| - jsAst.Expression constantInitializerExpression(ConstantValue value) {
|
| - return constantEmitter.initializationExpression(value);
|
| - }
|
| -
|
| - String get name => 'CodeEmitter';
|
| -
|
| - String get currentGenerateAccessorName
|
| - => '${namer.currentIsolate}.\$generateAccessor';
|
| - String get generateAccessorHolder
|
| - => '$isolatePropertiesName.\$generateAccessor';
|
| - String get finishClassesProperty
|
| - => r'$finishClasses';
|
| - String get finishClassesName
|
| - => '${namer.isolateName}.$finishClassesProperty';
|
| - String get finishIsolateConstructorName
|
| - => '${namer.isolateName}.\$finishIsolateConstructor';
|
| - String get isolatePropertiesName
|
| - => '${namer.isolateName}.${namer.isolatePropertiesName}';
|
| - String get lazyInitializerName
|
| - => '${namer.isolateName}.\$lazy';
|
| - String get initName => 'init';
|
| - String get makeConstListProperty
|
| - => namer.getMappedInstanceName('makeConstantList');
|
| -
|
| - /// For deferred loading we communicate the initializers via this global var.
|
| - final String deferredInitializers = r"$dart_deferred_initializers";
|
| -
|
| - /// All the global state can be passed around with this variable.
|
| - String get globalsHolder => namer.getMappedGlobalName("globalsHolder");
|
| -
|
| - jsAst.Expression generateEmbeddedGlobalAccess(String global) {
|
| - return js(generateEmbeddedGlobalAccessString(global));
|
| - }
|
| -
|
| - String generateEmbeddedGlobalAccessString(String global) {
|
| - // TODO(floitsch): don't use 'init' as global embedder storage.
|
| - return '$initName.$global';
|
| - }
|
| -
|
| - jsAst.FunctionDeclaration get generateAccessorFunction {
|
| - const RANGE1_SIZE = RANGE1_LAST - RANGE1_FIRST + 1;
|
| - const RANGE2_SIZE = RANGE2_LAST - RANGE2_FIRST + 1;
|
| - const RANGE1_ADJUST = - (FIRST_FIELD_CODE - RANGE1_FIRST);
|
| - const RANGE2_ADJUST = - (FIRST_FIELD_CODE + RANGE1_SIZE - RANGE2_FIRST);
|
| - const RANGE3_ADJUST =
|
| - - (FIRST_FIELD_CODE + RANGE1_SIZE + RANGE2_SIZE - RANGE3_FIRST);
|
| -
|
| - String receiverParamName = compiler.enableMinification ? "r" : "receiver";
|
| - String valueParamName = compiler.enableMinification ? "v" : "value";
|
| - String reflectableField = namer.reflectableField;
|
| -
|
| - return js.statement('''
|
| - function generateAccessor(fieldDescriptor, accessors, cls) {
|
| - var fieldInformation = fieldDescriptor.split("-");
|
| - var field = fieldInformation[0];
|
| - var len = field.length;
|
| - var code = field.charCodeAt(len - 1);
|
| - var reflectable;
|
| - if (fieldInformation.length > 1) reflectable = true;
|
| - else reflectable = false;
|
| - code = ((code >= $RANGE1_FIRST) && (code <= $RANGE1_LAST))
|
| - ? code - $RANGE1_ADJUST
|
| - : ((code >= $RANGE2_FIRST) && (code <= $RANGE2_LAST))
|
| - ? code - $RANGE2_ADJUST
|
| - : ((code >= $RANGE3_FIRST) && (code <= $RANGE3_LAST))
|
| - ? code - $RANGE3_ADJUST
|
| - : $NO_FIELD_CODE;
|
| -
|
| - if (code) { // needsAccessor
|
| - var getterCode = code & 3;
|
| - var setterCode = code >> 2;
|
| - var accessorName = field = field.substring(0, len - 1);
|
| -
|
| - var divider = field.indexOf(":");
|
| - if (divider > 0) { // Colon never in first position.
|
| - accessorName = field.substring(0, divider);
|
| - field = field.substring(divider + 1);
|
| - }
|
| -
|
| - if (getterCode) { // needsGetter
|
| - var args = (getterCode & 2) ? "$receiverParamName" : "";
|
| - var receiver = (getterCode & 1) ? "this" : "$receiverParamName";
|
| - var body = "return " + receiver + "." + field;
|
| - var property =
|
| - cls + ".prototype.${namer.getterPrefix}" + accessorName + "=";
|
| - var fn = "function(" + args + "){" + body + "}";
|
| - if (reflectable)
|
| - accessors.push(property + "\$reflectable(" + fn + ");\\n");
|
| - else
|
| - accessors.push(property + fn + ";\\n");
|
| - }
|
| -
|
| - if (setterCode) { // needsSetter
|
| - var args = (setterCode & 2)
|
| - ? "$receiverParamName,${_}$valueParamName"
|
| - : "$valueParamName";
|
| - var receiver = (setterCode & 1) ? "this" : "$receiverParamName";
|
| - var body = receiver + "." + field + "$_=$_$valueParamName";
|
| - var property =
|
| - cls + ".prototype.${namer.setterPrefix}" + accessorName + "=";
|
| - var fn = "function(" + args + "){" + body + "}";
|
| - if (reflectable)
|
| - accessors.push(property + "\$reflectable(" + fn + ");\\n");
|
| - else
|
| - accessors.push(property + fn + ";\\n");
|
| - }
|
| - }
|
| -
|
| - return field;
|
| - }''');
|
| - }
|
| -
|
| - List get defineClassFunction {
|
| - // First the class name, then the field names in an array and the members
|
| - // (inside an Object literal).
|
| - // The caller can also pass in the constructor as a function if needed.
|
| - //
|
| - // Example:
|
| - // defineClass("A", ["x", "y"], {
|
| - // foo$1: function(y) {
|
| - // print(this.x + y);
|
| - // },
|
| - // bar$2: function(t, v) {
|
| - // this.x = t - v;
|
| - // },
|
| - // });
|
| -
|
| - var defineClass = js('''function(name, cls, fields) {
|
| - var accessors = [];
|
| -
|
| - var str = "function " + cls + "(";
|
| - var body = "";
|
| -
|
| - for (var i = 0; i < fields.length; i++) {
|
| - if(i != 0) str += ", ";
|
| -
|
| - var field = generateAccessor(fields[i], accessors, cls);
|
| - var parameter = "parameter_" + field;
|
| - str += parameter;
|
| - body += ("this." + field + " = " + parameter + ";\\n");
|
| - }
|
| - str += ") {\\n" + body + "}\\n";
|
| - str += cls + ".builtin\$cls=\\"" + name + "\\";\\n";
|
| - str += "\$desc=\$collectedClasses." + cls + ";\\n";
|
| - str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n";
|
| - str += cls + ".prototype = \$desc;\\n";
|
| - if (typeof defineClass.name != "string") {
|
| - str += cls + ".name=\\"" + cls + "\\";\\n";
|
| - }
|
| - str += accessors.join("");
|
| -
|
| - return str;
|
| - }''');
|
| - // Declare a function called "generateAccessor". This is used in
|
| - // defineClassFunction (it's a local declaration in init()).
|
| - return [
|
| - generateAccessorFunction,
|
| - js('$generateAccessorHolder = generateAccessor'),
|
| - new jsAst.FunctionDeclaration(
|
| - new jsAst.VariableDeclaration('defineClass'), defineClass) ];
|
| - }
|
| -
|
| - /** Needs defineClass to be defined. */
|
| - List buildInheritFrom() {
|
| - return [js(r'''
|
| - var inheritFrom = function() {
|
| - function tmp() {}
|
| - var hasOwnProperty = Object.prototype.hasOwnProperty;
|
| - return function (constructor, superConstructor) {
|
| - tmp.prototype = superConstructor.prototype;
|
| - var object = new tmp();
|
| - var properties = constructor.prototype;
|
| - for (var member in properties)
|
| - if (hasOwnProperty.call(properties, member))
|
| - object[member] = properties[member];
|
| - object.constructor = constructor;
|
| - constructor.prototype = object;
|
| - return object;
|
| - };
|
| - }()
|
| - ''')];
|
| - }
|
| -
|
| - jsAst.Fun get finishClassesFunction {
|
| - // Class descriptions are collected in a JS object.
|
| - // 'finishClasses' takes all collected descriptions and sets up
|
| - // the prototype.
|
| - // Once set up, the constructors prototype field satisfy:
|
| - // - it contains all (local) members.
|
| - // - its internal prototype (__proto__) points to the superclass'
|
| - // prototype field.
|
| - // - the prototype's constructor field points to the JavaScript
|
| - // constructor.
|
| - // For engines where we have access to the '__proto__' we can manipulate
|
| - // the object literal directly. For other engines we have to create a new
|
| - // object and copy over the members.
|
| -
|
| - String reflectableField = namer.reflectableField;
|
| - jsAst.Expression allClassesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.ALL_CLASSES);
|
| - jsAst.Expression metadataAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.METADATA);
|
| - jsAst.Expression interceptorsByTagAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG);
|
| - jsAst.Expression leafTagsAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS);
|
| -
|
| - return js('''
|
| - function(collectedClasses, isolateProperties, existingIsolateProperties) {
|
| - var pendingClasses = Object.create(null);
|
| - if (!#) # = Object.create(null); // embedded allClasses.
|
| - var allClasses = #; // embedded allClasses;
|
| -
|
| - if (#) // DEBUG_FAST_OBJECTS
|
| - print("Number of classes: " +
|
| - Object.getOwnPropertyNames(\$\$).length);
|
| -
|
| - var hasOwnProperty = Object.prototype.hasOwnProperty;
|
| -
|
| - if (typeof dart_precompiled == "function") {
|
| - var constructors = dart_precompiled(collectedClasses);
|
| - } else {
|
| - var combinedConstructorFunction =
|
| - "function \$reflectable(fn){fn.$reflectableField=1;return fn};\\n"+
|
| - "var \$desc;\\n";
|
| - var constructorsList = [];
|
| - }
|
| -
|
| - for (var cls in collectedClasses) {
|
| - var desc = collectedClasses[cls];
|
| - if (desc instanceof Array) desc = desc[1];
|
| -
|
| - /* The 'fields' are either a constructor function or a
|
| - * string encoding fields, constructor and superclass. Get
|
| - * the superclass and the fields in the format
|
| - * '[name/]Super;field1,field2'
|
| - * from the CLASS_DESCRIPTOR_PROPERTY property on the descriptor.
|
| - * The 'name/' is optional and contains the name that should be used
|
| - * when printing the runtime type string. It is used, for example,
|
| - * to print the runtime type JSInt as 'int'.
|
| - */
|
| - var classData = desc["${namer.classDescriptorProperty}"],
|
| - supr, name = cls, fields = classData;
|
| - if (#) // backend.hasRetainedMetadata
|
| - if (typeof classData == "object" &&
|
| - classData instanceof Array) {
|
| - classData = fields = classData[0];
|
| - }
|
| - if (typeof classData == "string") {
|
| - var split = classData.split("/");
|
| - if (split.length == 2) {
|
| - name = split[0];
|
| - fields = split[1];
|
| - }
|
| - }
|
| -
|
| - var s = fields.split(";");
|
| - fields = s[1] == "" ? [] : s[1].split(",");
|
| - supr = s[0];
|
| - split = supr.split(":");
|
| - if (split.length == 2) {
|
| - supr = split[0];
|
| - var functionSignature = split[1];
|
| - if (functionSignature)
|
| - desc.\$signature = (function(s) {
|
| - return function(){ return #[s]; }; // embedded metadata.
|
| - })(functionSignature);
|
| - }
|
| -
|
| - if (#) // needsMixinSupport
|
| - if (supr && supr.indexOf("+") > 0) {
|
| - s = supr.split("+");
|
| - supr = s[0];
|
| - var mixin = collectedClasses[s[1]];
|
| - if (mixin instanceof Array) mixin = mixin[1];
|
| - for (var d in mixin) {
|
| - if (hasOwnProperty.call(mixin, d) &&
|
| - !hasOwnProperty.call(desc, d))
|
| - desc[d] = mixin[d];
|
| - }
|
| - }
|
| -
|
| - if (typeof dart_precompiled != "function") {
|
| - combinedConstructorFunction += defineClass(name, cls, fields);
|
| - constructorsList.push(cls);
|
| - }
|
| - if (supr) pendingClasses[cls] = supr;
|
| - }
|
| -
|
| - if (typeof dart_precompiled != "function") {
|
| - combinedConstructorFunction +=
|
| - "return [\\n " + constructorsList.join(",\\n ") + "\\n]";
|
| - var constructors =
|
| - new Function("\$collectedClasses", combinedConstructorFunction)
|
| - (collectedClasses);
|
| - combinedConstructorFunction = null;
|
| - }
|
| -
|
| - for (var i = 0; i < constructors.length; i++) {
|
| - var constructor = constructors[i];
|
| - var cls = constructor.name;
|
| - var desc = collectedClasses[cls];
|
| - var globalObject = isolateProperties;
|
| - if (desc instanceof Array) {
|
| - globalObject = desc[0] || isolateProperties;
|
| - desc = desc[1];
|
| - }
|
| - if (#) //backend.isTreeShakingDisabled,
|
| - constructor["${namer.metadataField}"] = desc;
|
| - allClasses[cls] = constructor;
|
| - globalObject[cls] = constructor;
|
| - }
|
| -
|
| - constructors = null;
|
| -
|
| - var finishedClasses = Object.create(null);
|
| - # = Object.create(null); // embedded interceptorsByTag.
|
| - # = Object.create(null); // embedded leafTags.
|
| -
|
| - #; // buildFinishClass(),
|
| -
|
| - #; // buildTrivialNsmHandlers()
|
| -
|
| - for (var cls in pendingClasses) finishClass(cls);
|
| - }''', [
|
| - allClassesAccess, allClassesAccess,
|
| - allClassesAccess,
|
| - DEBUG_FAST_OBJECTS,
|
| - backend.hasRetainedMetadata,
|
| - metadataAccess,
|
| - needsMixinSupport,
|
| - backend.isTreeShakingDisabled,
|
| - interceptorsByTagAccess,
|
| - leafTagsAccess,
|
| - buildFinishClass(),
|
| - nsmEmitter.buildTrivialNsmHandlers()]);
|
| - }
|
| -
|
| - jsAst.Node optional(bool condition, jsAst.Node node) {
|
| - return condition ? node : new jsAst.EmptyStatement();
|
| - }
|
| -
|
| - jsAst.FunctionDeclaration buildFinishClass() {
|
| - String specProperty = '"${namer.nativeSpecProperty}"'; // "%"
|
| -
|
| - jsAst.Expression interceptorsByTagAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG);
|
| - jsAst.Expression leafTagsAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS);
|
| -
|
| - return js.statement('''
|
| - function finishClass(cls) {
|
| -
|
| - if (finishedClasses[cls]) return;
|
| - finishedClasses[cls] = true;
|
| -
|
| - var superclass = pendingClasses[cls];
|
| -
|
| - // The superclass is only false (empty string) for the Dart Object
|
| - // class. The minifier together with noSuchMethod can put methods on
|
| - // the Object.prototype object, and they show through here, so we check
|
| - // that we have a string.
|
| - if (!superclass || typeof superclass != "string") return;
|
| - finishClass(superclass);
|
| - var constructor = allClasses[cls];
|
| - var superConstructor = allClasses[superclass];
|
| -
|
| - if (!superConstructor)
|
| - superConstructor = existingIsolateProperties[superclass];
|
| -
|
| - var prototype = inheritFrom(constructor, superConstructor);
|
| -
|
| - if (#) { // !nativeClasses.isEmpty,
|
| - // The property looks like this:
|
| - //
|
| - // HtmlElement: {
|
| - // "%": "HTMLDivElement|HTMLAnchorElement;HTMLElement;FancyButton"
|
| - //
|
| - // The first two semicolon-separated parts contain dispatch tags, the
|
| - // third contains the JavaScript names for classes.
|
| - //
|
| - // The tags indicate that JavaScript objects with the dispatch tags
|
| - // (usually constructor names) HTMLDivElement, HTMLAnchorElement and
|
| - // HTMLElement all map to the Dart native class named HtmlElement.
|
| - // The first set is for effective leaf nodes in the hierarchy, the
|
| - // second set is non-leaf nodes.
|
| - //
|
| - // The third part contains the JavaScript names of Dart classes that
|
| - // extend the native class. Here, FancyButton extends HtmlElement, so
|
| - // the runtime needs to know that window.HTMLElement.prototype is the
|
| - // prototype that needs to be extended in creating the custom element.
|
| - //
|
| - // The information is used to build tables referenced by
|
| - // getNativeInterceptor and custom element support.
|
| - if (Object.prototype.hasOwnProperty.call(prototype, $specProperty)) {
|
| - var nativeSpec = prototype[$specProperty].split(";");
|
| - if (nativeSpec[0]) {
|
| - var tags = nativeSpec[0].split("|");
|
| - for (var i = 0; i < tags.length; i++) {
|
| - #[tags[i]] = constructor; // embedded interceptorsByTag.
|
| - #[tags[i]] = true; // embedded leafTags.
|
| - }
|
| - }
|
| - if (nativeSpec[1]) {
|
| - tags = nativeSpec[1].split("|");
|
| - if (#) { // User subclassing of native classes?
|
| - if (nativeSpec[2]) {
|
| - var subclasses = nativeSpec[2].split("|");
|
| - for (var i = 0; i < subclasses.length; i++) {
|
| - var subclass = allClasses[subclasses[i]];
|
| - subclass.\$nativeSuperclassTag = tags[0];
|
| - }
|
| - }
|
| - for (i = 0; i < tags.length; i++) {
|
| - #[tags[i]] = constructor; // embedded interceptorsByTag.
|
| - #[tags[i]] = false; // embedded leafTags.
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }''', [!nativeClasses.isEmpty,
|
| - interceptorsByTagAccess,
|
| - leafTagsAccess,
|
| - true,
|
| - interceptorsByTagAccess,
|
| - leafTagsAccess]);
|
| - }
|
| -
|
| - jsAst.Fun get finishIsolateConstructorFunction {
|
| - // We replace the old Isolate function with a new one that initializes
|
| - // all its fields with the initial (and often final) value of all globals.
|
| - //
|
| - // We also copy over old values like the prototype, and the
|
| - // isolateProperties themselves.
|
| - return js('''
|
| - function (oldIsolate) {
|
| - var isolateProperties = oldIsolate.#; // isolatePropertiesName
|
| - function Isolate() {
|
| - var hasOwnProperty = Object.prototype.hasOwnProperty;
|
| - for (var staticName in isolateProperties)
|
| - if (hasOwnProperty.call(isolateProperties, staticName))
|
| - this[staticName] = isolateProperties[staticName];
|
| -
|
| - // Reset lazy initializers to null.
|
| - // When forcing the object to fast mode (below) v8 will consider
|
| - // functions as part the object's map. Since we will change them
|
| - // (after the first call to the getter), we would have a map
|
| - // transition.
|
| - var lazies = init.lazies;
|
| - for (var lazyInit in lazies) {
|
| - this[lazies[lazyInit]] = null;
|
| - }
|
| -
|
| - // Use the newly created object as prototype. In Chrome,
|
| - // this creates a hidden class for the object and makes
|
| - // sure it is fast to access.
|
| - function ForceEfficientMap() {}
|
| - ForceEfficientMap.prototype = this;
|
| - new ForceEfficientMap();
|
| -
|
| - // Now, after being a fast map we can set the lazies again.
|
| - for (var lazyInit in lazies) {
|
| - var lazyInitName = lazies[lazyInit];
|
| - this[lazyInitName] = isolateProperties[lazyInitName];
|
| - }
|
| - }
|
| - Isolate.prototype = oldIsolate.prototype;
|
| - Isolate.prototype.constructor = Isolate;
|
| - Isolate.# = isolateProperties; // isolatePropertiesName
|
| - if (#) // needsDefineClass.
|
| - Isolate.# = oldIsolate.#; // finishClassesProperty * 2
|
| - if (#) // outputContainsConstantList
|
| - Isolate.# = oldIsolate.#; // makeConstListProperty * 2
|
| - return Isolate;
|
| - }''',
|
| - [namer.isolatePropertiesName, namer.isolatePropertiesName,
|
| - needsDefineClass, finishClassesProperty, finishClassesProperty,
|
| - task.outputContainsConstantList,
|
| - makeConstListProperty, makeConstListProperty ]);
|
| - }
|
| -
|
| - jsAst.Fun get lazyInitializerFunction {
|
| - String isolate = namer.currentIsolate;
|
| - jsAst.Expression cyclicThrow =
|
| - namer.elementAccess(backend.getCyclicThrowHelper());
|
| - jsAst.Expression laziesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.LAZIES);
|
| -
|
| - return js('''
|
| - function (prototype, staticName, fieldName, getterName, lazyValue) {
|
| - if (!#) # = Object.create(null);
|
| - #[fieldName] = getterName;
|
| -
|
| - var sentinelUndefined = {};
|
| - var sentinelInProgress = {};
|
| - prototype[fieldName] = sentinelUndefined;
|
| -
|
| - prototype[getterName] = function () {
|
| - var result = $isolate[fieldName];
|
| - try {
|
| - if (result === sentinelUndefined) {
|
| - $isolate[fieldName] = sentinelInProgress;
|
| -
|
| - try {
|
| - result = $isolate[fieldName] = lazyValue();
|
| - } finally {
|
| - // Use try-finally, not try-catch/throw as it destroys the
|
| - // stack trace.
|
| - if (result === sentinelUndefined)
|
| - $isolate[fieldName] = null;
|
| - }
|
| - } else {
|
| - if (result === sentinelInProgress)
|
| - #(staticName);
|
| - }
|
| -
|
| - return result;
|
| - } finally {
|
| - $isolate[getterName] = function() { return this[fieldName]; };
|
| - }
|
| - }
|
| - }
|
| - ''', [laziesAccess, laziesAccess,
|
| - laziesAccess,
|
| - cyclicThrow]);
|
| - }
|
| -
|
| - List buildDefineClassAndFinishClassFunctionsIfNecessary() {
|
| - if (!needsDefineClass) return [];
|
| - return defineClassFunction
|
| - ..addAll(buildInheritFrom())
|
| - ..addAll([
|
| - js('$finishClassesName = #', finishClassesFunction)
|
| - ]);
|
| - }
|
| -
|
| - List buildLazyInitializerFunctionIfNecessary() {
|
| - if (!needsLazyInitializer) return [];
|
| -
|
| - return [js('# = #', [js(lazyInitializerName), lazyInitializerFunction])];
|
| - }
|
| -
|
| - List buildFinishIsolateConstructor() {
|
| - return [
|
| - js('$finishIsolateConstructorName = #', finishIsolateConstructorFunction)
|
| - ];
|
| - }
|
| -
|
| - void emitFinishIsolateConstructorInvocation(CodeBuffer buffer) {
|
| - String isolate = namer.isolateName;
|
| - buffer.write("$isolate = $finishIsolateConstructorName($isolate)$N");
|
| - }
|
| -
|
| - /// In minified mode we want to keep the name for the most common core types.
|
| - bool _isNativeTypeNeedingReflectionName(Element element) {
|
| - if (!element.isClass) return false;
|
| - return (element == compiler.intClass ||
|
| - element == compiler.doubleClass ||
|
| - element == compiler.numClass ||
|
| - element == compiler.stringClass ||
|
| - element == compiler.boolClass ||
|
| - element == compiler.nullClass ||
|
| - element == compiler.listClass);
|
| - }
|
| -
|
| - /// Returns the "reflection name" of an [Element] or [Selector].
|
| - /// The reflection name of a getter 'foo' is 'foo'.
|
| - /// The reflection name of a setter 'foo' is 'foo='.
|
| - /// The reflection name of a method 'foo' is 'foo:N:M:O', where N is the
|
| - /// number of required arguments, M is the number of optional arguments, and
|
| - /// O is the named arguments.
|
| - /// The reflection name of a constructor is similar to a regular method but
|
| - /// starts with 'new '.
|
| - /// The reflection name of class 'C' is 'C'.
|
| - /// An anonymous mixin application has no reflection name.
|
| - /// This is used by js_mirrors.dart.
|
| - String getReflectionName(elementOrSelector, String mangledName) {
|
| - String name = elementOrSelector.name;
|
| - if (backend.shouldRetainName(name) ||
|
| - elementOrSelector is Element &&
|
| - // Make sure to retain names of unnamed constructors, and
|
| - // for common native types.
|
| - ((name == '' &&
|
| - backend.isAccessibleByReflection(elementOrSelector)) ||
|
| - _isNativeTypeNeedingReflectionName(elementOrSelector))) {
|
| -
|
| - // TODO(ahe): Enable the next line when I can tell the difference between
|
| - // an instance method and a global. They may have the same mangled name.
|
| - // if (recordedMangledNames.contains(mangledName)) return null;
|
| - recordedMangledNames.add(mangledName);
|
| - return getReflectionNameInternal(elementOrSelector, mangledName);
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - String getReflectionNameInternal(elementOrSelector, String mangledName) {
|
| - String name =
|
| - namer.privateName(elementOrSelector.library, elementOrSelector.name);
|
| - if (elementOrSelector.isGetter) return name;
|
| - if (elementOrSelector.isSetter) {
|
| - if (!mangledName.startsWith(namer.setterPrefix)) return '$name=';
|
| - String base = mangledName.substring(namer.setterPrefix.length);
|
| - String getter = '${namer.getterPrefix}$base';
|
| - mangledFieldNames.putIfAbsent(getter, () => name);
|
| - assert(mangledFieldNames[getter] == name);
|
| - recordedMangledNames.add(getter);
|
| - // TODO(karlklose,ahe): we do not actually need to store information
|
| - // about the name of this setter in the output, but it is needed for
|
| - // marking the function as invokable by reflection.
|
| - return '$name=';
|
| - }
|
| - if (elementOrSelector is Element && elementOrSelector.isClosure) {
|
| - // Closures are synthesized and their name might conflict with existing
|
| - // globals. Assign an illegal name, and make sure they don't clash
|
| - // with each other.
|
| - return " $mangledName";
|
| - }
|
| - if (elementOrSelector is Selector
|
| - || elementOrSelector.isFunction
|
| - || elementOrSelector.isConstructor) {
|
| - int requiredParameterCount;
|
| - int optionalParameterCount;
|
| - String namedArguments = '';
|
| - bool isConstructor = false;
|
| - if (elementOrSelector is Selector) {
|
| - Selector selector = elementOrSelector;
|
| - requiredParameterCount = selector.argumentCount;
|
| - optionalParameterCount = 0;
|
| - namedArguments = namedParametersAsReflectionNames(selector);
|
| - } else {
|
| - FunctionElement function = elementOrSelector;
|
| - if (function.isConstructor) {
|
| - isConstructor = true;
|
| - name = Elements.reconstructConstructorName(function);
|
| - }
|
| - FunctionSignature signature = function.functionSignature;
|
| - requiredParameterCount = signature.requiredParameterCount;
|
| - optionalParameterCount = signature.optionalParameterCount;
|
| - if (signature.optionalParametersAreNamed) {
|
| - var names = [];
|
| - for (Element e in signature.optionalParameters) {
|
| - names.add(e.name);
|
| - }
|
| - Selector selector = new Selector.call(
|
| - function.name,
|
| - function.library,
|
| - requiredParameterCount,
|
| - names);
|
| - namedArguments = namedParametersAsReflectionNames(selector);
|
| - } else {
|
| - // Named parameters are handled differently by mirrors. For unnamed
|
| - // parameters, they are actually required if invoked
|
| - // reflectively. Also, if you have a method c(x) and c([x]) they both
|
| - // get the same mangled name, so they must have the same reflection
|
| - // name.
|
| - requiredParameterCount += optionalParameterCount;
|
| - optionalParameterCount = 0;
|
| - }
|
| - }
|
| - String suffix =
|
| - // TODO(ahe): We probably don't need optionalParameterCount in the
|
| - // reflection name.
|
| - '$name:$requiredParameterCount:$optionalParameterCount'
|
| - '$namedArguments';
|
| - return (isConstructor) ? 'new $suffix' : suffix;
|
| - }
|
| - Element element = elementOrSelector;
|
| - if (element.isGenerativeConstructorBody) {
|
| - return null;
|
| - } else if (element.isClass) {
|
| - ClassElement cls = element;
|
| - if (cls.isUnnamedMixinApplication) return null;
|
| - return cls.name;
|
| - } else if (element.isTypedef) {
|
| - return element.name;
|
| - }
|
| - throw compiler.internalError(element,
|
| - 'Do not know how to reflect on this $element.');
|
| - }
|
| -
|
| - String namedParametersAsReflectionNames(Selector selector) {
|
| - if (selector.getOrderedNamedArguments().isEmpty) return '';
|
| - String names = selector.getOrderedNamedArguments().join(':');
|
| - return ':$names';
|
| - }
|
| -
|
| - jsAst.FunctionDeclaration buildCspPrecompiledFunctionFor(
|
| - OutputUnit outputUnit) {
|
| - // TODO(ahe): Compute a hash code.
|
| - return js.statement('''
|
| - function dart_precompiled(\$collectedClasses) {
|
| - var \$desc;
|
| - #;
|
| - return #;
|
| - }''',
|
| - [cspPrecompiledFunctionFor(outputUnit),
|
| - new jsAst.ArrayInitializer.from(
|
| - cspPrecompiledConstructorNamesFor(outputUnit))]);
|
| - }
|
| -
|
| - void generateClass(ClassElement classElement, ClassBuilder properties) {
|
| - compiler.withCurrentElement(classElement, () {
|
| - if (compiler.hasIncrementalSupport) {
|
| - ClassBuilder builder =
|
| - cachedClassBuilders.putIfAbsent(classElement, () {
|
| - ClassBuilder builder = new ClassBuilder(classElement, namer);
|
| - classEmitter.generateClass(
|
| - classElement, builder, additionalProperties[classElement]);
|
| - return builder;
|
| - });
|
| - invariant(classElement, builder.fields.isEmpty);
|
| - invariant(classElement, builder.superName == null);
|
| - invariant(classElement, builder.functionType == null);
|
| - invariant(classElement, builder.fieldMetadata == null);
|
| - properties.properties.addAll(builder.properties);
|
| - } else {
|
| - classEmitter.generateClass(
|
| - classElement, properties, additionalProperties[classElement]);
|
| - }
|
| - });
|
| - }
|
| -
|
| - void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) {
|
| - if (needsDefineClass) {
|
| - buffer.write('$finishClassesName($classesCollector,'
|
| - '$_$isolateProperties,'
|
| - '${_}null)$N');
|
| -
|
| - // Reset the map.
|
| - buffer.write("$classesCollector$_=${_}null$N$n");
|
| - }
|
| - }
|
| -
|
| - void emitStaticFunctions(List<Element> staticFunctions) {
|
| - for (Element element in staticFunctions) {
|
| - ClassBuilder builder = new ClassBuilder(element, namer);
|
| - containerBuilder.addMember(element, builder);
|
| - getElementDescriptor(element).properties.addAll(builder.properties);
|
| - }
|
| - }
|
| -
|
| - void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) {
|
| - JavaScriptConstantCompiler handler = backend.constants;
|
| - Iterable<VariableElement> staticNonFinalFields =
|
| - handler.getStaticNonFinalFieldsForEmission();
|
| - for (Element element in Elements.sortedByPosition(staticNonFinalFields)) {
|
| - // [:interceptedNames:] is handled in [emitInterceptedNames].
|
| - if (element == backend.interceptedNames) continue;
|
| - // `mapTypeToInterceptor` is handled in [emitMapTypeToInterceptor].
|
| - if (element == backend.mapTypeToInterceptor) continue;
|
| - compiler.withCurrentElement(element, () {
|
| - ConstantValue initialValue = handler.getInitialValueFor(element).value;
|
| - jsAst.Expression init =
|
| - js('$isolateProperties.# = #',
|
| - [namer.getNameOfGlobalField(element),
|
| - constantEmitter.referenceInInitializationContext(initialValue)]);
|
| - buffer.write(jsAst.prettyPrint(init, compiler,
|
| - monitor: compiler.dumpInfoTask));
|
| - buffer.write('$N');
|
| - });
|
| - }
|
| - }
|
| -
|
| - void emitLazilyInitializedStaticFields(CodeBuffer buffer) {
|
| - JavaScriptConstantCompiler handler = backend.constants;
|
| - List<VariableElement> lazyFields =
|
| - handler.getLazilyInitializedFieldsForEmission();
|
| - if (!lazyFields.isEmpty) {
|
| - needsLazyInitializer = true;
|
| - for (VariableElement element in Elements.sortedByPosition(lazyFields)) {
|
| - jsAst.Expression code = backend.generatedCode[element];
|
| - // The code is null if we ended up not needing the lazily
|
| - // initialized field after all because of constant folding
|
| - // before code generation.
|
| - if (code == null) continue;
|
| - // The code only computes the initial value. We build the lazy-check
|
| - // here:
|
| - // lazyInitializer(prototype, 'name', fieldName, getterName, initial);
|
| - // The name is used for error reporting. The 'initial' must be a
|
| - // closure that constructs the initial value.
|
| - jsAst.Expression init = js('#(#,#,#,#,#)',
|
| - [js(lazyInitializerName),
|
| - js(isolateProperties),
|
| - js.string(element.name),
|
| - js.string(namer.getNameX(element)),
|
| - js.string(namer.getLazyInitializerName(element)),
|
| - code]);
|
| - buffer.write(jsAst.prettyPrint(init, compiler,
|
| - monitor: compiler.dumpInfoTask));
|
| - buffer.write("$N");
|
| - }
|
| - }
|
| - }
|
| -
|
| - bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) {
|
| - if (constant.isFunction) return true; // Already emitted.
|
| - if (constant.isPrimitive) return true; // Inlined.
|
| - if (constant.isDummy) return true; // Inlined.
|
| - // The name is null when the constant is already a JS constant.
|
| - // TODO(floitsch): every constant should be registered, so that we can
|
| - // share the ones that take up too much space (like some strings).
|
| - if (namer.constantName(constant) == null) return true;
|
| - return false;
|
| - }
|
| -
|
| - int compareConstants(ConstantValue a, ConstantValue b) {
|
| - // Inlined constants don't affect the order and sometimes don't even have
|
| - // names.
|
| - int cmp1 = isConstantInlinedOrAlreadyEmitted(a) ? 0 : 1;
|
| - int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1;
|
| - if (cmp1 + cmp2 < 2) return cmp1 - cmp2;
|
| -
|
| - // Emit constant interceptors first. Constant interceptors for primitives
|
| - // might be used by code that builds other constants. See Issue 18173.
|
| - if (a.isInterceptor != b.isInterceptor) {
|
| - return a.isInterceptor ? -1 : 1;
|
| - }
|
| -
|
| - // Sorting by the long name clusters constants with the same constructor
|
| - // which compresses a tiny bit better.
|
| - int r = namer.constantLongName(a).compareTo(namer.constantLongName(b));
|
| - if (r != 0) return r;
|
| - // Resolve collisions in the long name by using the constant name (i.e. JS
|
| - // name) which is unique.
|
| - return namer.constantName(a).compareTo(namer.constantName(b));
|
| - }
|
| -
|
| - void emitCompileTimeConstants(CodeBuffer buffer, OutputUnit outputUnit) {
|
| - List<ConstantValue> constants = outputConstantLists[outputUnit];
|
| - if (constants == null) return;
|
| - bool isMainBuffer = buffer == mainBuffer;
|
| - if (compiler.hasIncrementalSupport && isMainBuffer) {
|
| - buffer = cachedEmittedConstantsBuffer;
|
| - }
|
| - for (ConstantValue constant in constants) {
|
| - if (compiler.hasIncrementalSupport && isMainBuffer) {
|
| - if (cachedEmittedConstants.contains(constant)) continue;
|
| - cachedEmittedConstants.add(constant);
|
| - }
|
| - String name = namer.constantName(constant);
|
| - jsAst.Expression init = js('#.# = #',
|
| - [namer.globalObjectForConstant(constant), name,
|
| - constantInitializerExpression(constant)]);
|
| - buffer.write(jsAst.prettyPrint(init, compiler,
|
| - monitor: compiler.dumpInfoTask));
|
| - buffer.write('$N');
|
| - }
|
| - if (compiler.hasIncrementalSupport && isMainBuffer) {
|
| - mainBuffer.add(cachedEmittedConstantsBuffer);
|
| - }
|
| - }
|
| -
|
| - jsAst.Template get makeConstantListTemplate {
|
| - // TODO(floitsch): there is no harm in caching the template.
|
| - return jsAst.js.uncachedExpressionTemplate(
|
| - '${namer.isolateName}.$makeConstListProperty(#)');
|
| - }
|
| -
|
| - void emitMakeConstantList(CodeBuffer buffer) {
|
| - buffer.write(
|
| - jsAst.prettyPrint(
|
| - // Functions are stored in the hidden class and not as properties in
|
| - // the object. We never actually look at the value, but only want
|
| - // to know if the property exists.
|
| - js.statement(r'''#.# = function(list) {
|
| - list.immutable$list = Array;
|
| - list.fixed$length = Array;
|
| - return list;
|
| - }''',
|
| - [namer.isolateName, makeConstListProperty]),
|
| - compiler, monitor: compiler.dumpInfoTask));
|
| - buffer.write(N);
|
| - }
|
| -
|
| - /// Returns the code equivalent to:
|
| - /// `function(args) { $.startRootIsolate(X.main$closure(), args); }`
|
| - jsAst.Expression buildIsolateSetupClosure(Element appMain,
|
| - Element isolateMain) {
|
| - jsAst.Expression mainAccess = namer.isolateStaticClosureAccess(appMain);
|
| - // Since we pass the closurized version of the main method to
|
| - // the isolate method, we must make sure that it exists.
|
| - return js('function(a){ #(#, a); }',
|
| - [namer.elementAccess(isolateMain), mainAccess]);
|
| - }
|
| -
|
| - /**
|
| - * Emits code that sets the `isolateTag embedded global to a unique string.
|
| - */
|
| - jsAst.Expression generateIsolateAffinityTagInitialization() {
|
| - jsAst.Expression getIsolateTagAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.GET_ISOLATE_TAG);
|
| - jsAst.Expression isolateTagAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.ISOLATE_TAG);
|
| -
|
| - return js('''
|
| - !function() {
|
| - // On V8, the 'intern' function converts a string to a symbol, which
|
| - // makes property access much faster.
|
| - function intern(s) {
|
| - var o = {};
|
| - o[s] = 1;
|
| - return Object.keys(convertToFastObject(o))[0];
|
| - }
|
| -
|
| - # = function(name) { // embedded getIsolateTag
|
| - return intern("___dart_" + name + #); // embedded isolateTag
|
| - };
|
| -
|
| - // To ensure that different programs loaded into the same context (page)
|
| - // use distinct dispatch properies, we place an object on `Object` to
|
| - // contain the names already in use.
|
| - var tableProperty = "___dart_isolate_tags_";
|
| - var usedProperties = Object[tableProperty] ||
|
| - (Object[tableProperty] = Object.create(null));
|
| -
|
| - var rootProperty = "_${generateIsolateTagRoot()}";
|
| - for (var i = 0; ; i++) {
|
| - var property = intern(rootProperty + "_" + i + "_");
|
| - if (!(property in usedProperties)) {
|
| - usedProperties[property] = 1;
|
| - # = property; // embedded isolateTag
|
| - break;
|
| - }
|
| - }
|
| - }()
|
| - ''', [getIsolateTagAccess,
|
| - isolateTagAccess,
|
| - isolateTagAccess]);
|
| - }
|
| -
|
| - jsAst.Expression generateDispatchPropertyNameInitialization() {
|
| - jsAst.Expression dispatchPropertyNameAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.DISPATCH_PROPERTY_NAME);
|
| - jsAst.Expression getIsolateTagAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.GET_ISOLATE_TAG);
|
| - return js('# = #("dispatch_record")',
|
| - [dispatchPropertyNameAccess,
|
| - getIsolateTagAccess]);
|
| - }
|
| -
|
| - String generateIsolateTagRoot() {
|
| - // TODO(sra): MD5 of contributing source code or URIs?
|
| - return 'ZxYxX';
|
| - }
|
| -
|
| - emitMain(CodeBuffer buffer) {
|
| - if (compiler.isMockCompilation) return;
|
| - Element main = compiler.mainFunction;
|
| - jsAst.Expression mainCallClosure = null;
|
| - if (compiler.hasIsolateSupport) {
|
| - Element isolateMain =
|
| - backend.isolateHelperLibrary.find(JavaScriptBackend.START_ROOT_ISOLATE);
|
| - mainCallClosure = buildIsolateSetupClosure(main, isolateMain);
|
| - } else if (compiler.hasIncrementalSupport) {
|
| - mainCallClosure =
|
| - js('function() { return #(); }', namer.elementAccess(main));
|
| - } else {
|
| - mainCallClosure = namer.elementAccess(main);
|
| - }
|
| -
|
| - if (backend.needToInitializeIsolateAffinityTag) {
|
| - buffer.write(
|
| - jsAst.prettyPrint(generateIsolateAffinityTagInitialization(),
|
| - compiler, monitor: compiler.dumpInfoTask));
|
| - buffer.write(N);
|
| - }
|
| - if (backend.needToInitializeDispatchProperty) {
|
| - assert(backend.needToInitializeIsolateAffinityTag);
|
| - buffer.write(
|
| - jsAst.prettyPrint(generateDispatchPropertyNameInitialization(),
|
| - compiler, monitor: compiler.dumpInfoTask));
|
| - buffer.write(N);
|
| - }
|
| -
|
| - jsAst.Expression currentScriptAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.CURRENT_SCRIPT);
|
| -
|
| - addComment('BEGIN invoke [main].', buffer);
|
| - // This code finds the currently executing script by listening to the
|
| - // onload event of all script tags and getting the first script which
|
| - // finishes. Since onload is called immediately after execution this should
|
| - // not substantially change execution order.
|
| - jsAst.Statement invokeMain = js.statement('''
|
| -(function (callback) {
|
| - if (typeof document === "undefined") {
|
| - callback(null);
|
| - return;
|
| - }
|
| - if (document.currentScript) {
|
| - callback(document.currentScript);
|
| - return;
|
| - }
|
| -
|
| - var scripts = document.scripts;
|
| - function onLoad(event) {
|
| - for (var i = 0; i < scripts.length; ++i) {
|
| - scripts[i].removeEventListener("load", onLoad, false);
|
| - }
|
| - callback(event.target);
|
| - }
|
| - for (var i = 0; i < scripts.length; ++i) {
|
| - scripts[i].addEventListener("load", onLoad, false);
|
| - }
|
| -})(function(currentScript) {
|
| - # = currentScript; // embedded currentScript.
|
| -
|
| - if (typeof dartMainRunner === "function") {
|
| - dartMainRunner(#, []); // mainCallClosure.
|
| - } else {
|
| - #([]); // mainCallClosure.
|
| - }
|
| -})$N''', [currentScriptAccess,
|
| - mainCallClosure,
|
| - mainCallClosure]);
|
| -
|
| - buffer.write(';');
|
| - buffer.write(jsAst.prettyPrint(invokeMain,
|
| - compiler, monitor: compiler.dumpInfoTask));
|
| - buffer.write(N);
|
| - addComment('END invoke [main].', buffer);
|
| - }
|
| -
|
| - void emitInitFunction(CodeBuffer buffer) {
|
| - jsAst.FunctionDeclaration decl = js.statement('''
|
| - function init() {
|
| - $isolateProperties = Object.create(null);
|
| - #; #; #;
|
| - }''', [
|
| - buildDefineClassAndFinishClassFunctionsIfNecessary(),
|
| - buildLazyInitializerFunctionIfNecessary(),
|
| - buildFinishIsolateConstructor()]);
|
| -
|
| - buffer.write(jsAst.prettyPrint(decl,
|
| - compiler, monitor: compiler.dumpInfoTask).getText());
|
| - if (compiler.enableMinification) buffer.write('\n');
|
| - }
|
| -
|
| - void emitConvertToFastObjectFunction() {
|
| - List<jsAst.Statement> debugCode = <jsAst.Statement>[];
|
| - if (DEBUG_FAST_OBJECTS) {
|
| - debugCode.add(js.statement(r'''
|
| - // The following only works on V8 when run with option
|
| - // "--allow-natives-syntax". We use'new Function' because the
|
| - // miniparser does not understand V8 native syntax.
|
| - if (typeof print === "function") {
|
| - var HasFastProperties =
|
| - new Function("a", "return %HasFastProperties(a)");
|
| - print("Size of global object: "
|
| - + String(Object.getOwnPropertyNames(properties).length)
|
| - + ", fast properties " + HasFastProperties(properties));
|
| - }'''));
|
| - }
|
| -
|
| - jsAst.Statement convertToFastObject = js.statement(r'''
|
| - function convertToFastObject(properties) {
|
| - // Create an instance that uses 'properties' as prototype. This should
|
| - // make 'properties' a fast object.
|
| - function MyClass() {};
|
| - MyClass.prototype = properties;
|
| - new MyClass();
|
| - #;
|
| - return properties;
|
| - }''', [debugCode]);
|
| -
|
| - mainBuffer.add(jsAst.prettyPrint(convertToFastObject, compiler));
|
| - mainBuffer.add(N);
|
| - }
|
| -
|
| - void writeLibraryDescriptors(CodeBuffer buffer, LibraryElement library) {
|
| - var uri = "";
|
| - if (!compiler.enableMinification || backend.mustPreserveUris) {
|
| - uri = library.canonicalUri;
|
| - if (uri.scheme == 'file' && compiler.outputUri != null) {
|
| - uri = relativize(compiler.outputUri, library.canonicalUri, false);
|
| - }
|
| - }
|
| - ClassBuilder descriptor = elementDescriptors[library];
|
| - if (descriptor == null) {
|
| - // Nothing of the library was emitted.
|
| - // TODO(floitsch): this should not happen. We currently have an example
|
| - // with language/prefix6_negative_test.dart where we have an instance
|
| - // method without its corresponding class.
|
| - return;
|
| - }
|
| -
|
| - String libraryName =
|
| - (!compiler.enableMinification || backend.mustRetainLibraryNames) ?
|
| - library.getLibraryName() :
|
| - "";
|
| -
|
| - jsAst.Fun metadata = metadataEmitter.buildMetadataFunction(library);
|
| -
|
| - jsAst.ObjectInitializer initializers = descriptor.toObjectInitializer();
|
| -
|
| - compiler.dumpInfoTask.registerElementAst(library, metadata);
|
| - compiler.dumpInfoTask.registerElementAst(library, initializers);
|
| - buffer
|
| - ..write('["$libraryName",$_')
|
| - ..write('"${uri}",$_')
|
| - ..write(metadata == null ? "" : jsAst.prettyPrint(metadata,
|
| - compiler,
|
| - monitor: compiler.dumpInfoTask))
|
| - ..write(',$_')
|
| - ..write(namer.globalObjectFor(library))
|
| - ..write(',$_')
|
| - ..write(jsAst.prettyPrint(initializers,
|
| - compiler,
|
| - monitor: compiler.dumpInfoTask))
|
| - ..write(library == compiler.mainApp ? ',${n}1' : "")
|
| - ..write('],$n');
|
| - }
|
| -
|
| - void emitPrecompiledConstructor(OutputUnit outputUnit,
|
| - String constructorName,
|
| - jsAst.Expression constructorAst) {
|
| - cspPrecompiledFunctionFor(outputUnit).add(
|
| - new jsAst.FunctionDeclaration(
|
| - new jsAst.VariableDeclaration(constructorName), constructorAst));
|
| - cspPrecompiledFunctionFor(outputUnit).add(
|
| - js.statement(r'''{
|
| - #.builtin$cls = #;
|
| - if (!"name" in #)
|
| - #.name = #;
|
| - $desc=$collectedClasses.#;
|
| - if ($desc instanceof Array) $desc = $desc[1];
|
| - #.prototype = $desc;
|
| - }''',
|
| - [ constructorName, js.string(constructorName),
|
| - constructorName,
|
| - constructorName, js.string(constructorName),
|
| - constructorName,
|
| - constructorName
|
| - ]));
|
| -
|
| - cspPrecompiledConstructorNamesFor(outputUnit).add(js('#', constructorName));
|
| - }
|
| -
|
| - /// Extracts the output name of the compiler's outputUri.
|
| - String deferredPartFileName(OutputUnit outputUnit,
|
| - {bool addExtension: true}) {
|
| - String outPath = compiler.outputUri != null
|
| - ? compiler.outputUri.path
|
| - : "out";
|
| - String outName = outPath.substring(outPath.lastIndexOf('/') + 1);
|
| - String extension = addExtension ? ".part.js" : "";
|
| - if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) {
|
| - return "$outName$extension";
|
| - } else {
|
| - String name = outputUnit.name;
|
| - return "${outName}_$name$extension";
|
| - }
|
| - }
|
| -
|
| - void emitLibraries(Iterable<LibraryElement> libraries) {
|
| - if (libraries.isEmpty) return;
|
| -
|
| - // TODO(karlklose): document what kinds of fields this loop adds to the
|
| - // library class builder.
|
| - for (LibraryElement element in libraries) {
|
| - LibraryElement library = element;
|
| - ClassBuilder builder = new ClassBuilder(library, namer);
|
| - if (classEmitter.emitFields(library, builder, null, emitStatics: true)) {
|
| - jsAst.ObjectInitializer initializer = builder.toObjectInitializer();
|
| - compiler.dumpInfoTask.registerElementAst(builder.element, initializer);
|
| - getElementDescriptor(library).properties.addAll(initializer.properties);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void emitTypedefs() {
|
| - OutputUnit mainOutputUnit = compiler.deferredLoadTask.mainOutputUnit;
|
| -
|
| - // Emit all required typedef declarations into the main output unit.
|
| - // TODO(karlklose): unify required classes and typedefs to declarations
|
| - // and have builders for each kind.
|
| - for (TypedefElement typedef in typedefsNeededForReflection) {
|
| - OutputUnit mainUnit = compiler.deferredLoadTask.mainOutputUnit;
|
| - LibraryElement library = typedef.library;
|
| - // TODO(karlklose): add a TypedefBuilder and move this code there.
|
| - DartType type = typedef.alias;
|
| - int typeIndex = metadataEmitter.reifyType(type);
|
| - String typeReference =
|
| - encoding.encodeTypedefFieldDescriptor(typeIndex);
|
| - jsAst.Property descriptor = new jsAst.Property(
|
| - js.string(namer.classDescriptorProperty),
|
| - js.string(typeReference));
|
| - jsAst.Node declaration = new jsAst.ObjectInitializer([descriptor]);
|
| - String mangledName = namer.getNameX(typedef);
|
| - String reflectionName = getReflectionName(typedef, mangledName);
|
| - getElementDescriptor(library)
|
| - ..addProperty(mangledName, declaration)
|
| - ..addProperty("+$reflectionName", js.string(''));
|
| - // Also emit a trivial constructor for CSP mode.
|
| - String constructorName = mangledName;
|
| - jsAst.Expression constructorAst = js('function() {}');
|
| - emitPrecompiledConstructor(mainOutputUnit,
|
| - constructorName,
|
| - constructorAst);
|
| - }
|
| - }
|
| -
|
| - void emitMangledNames() {
|
| - if (!mangledFieldNames.isEmpty) {
|
| - var keys = mangledFieldNames.keys.toList();
|
| - keys.sort();
|
| - var properties = [];
|
| - for (String key in keys) {
|
| - var value = js.string('${mangledFieldNames[key]}');
|
| - properties.add(new jsAst.Property(js.string(key), value));
|
| - }
|
| -
|
| - jsAst.Expression mangledNamesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.MANGLED_NAMES);
|
| - var map = new jsAst.ObjectInitializer(properties);
|
| - mainBuffer.write(
|
| - jsAst.prettyPrint(
|
| - js.statement('# = #', [mangledNamesAccess, map]),
|
| - compiler,
|
| - monitor: compiler.dumpInfoTask));
|
| - if (compiler.enableMinification) {
|
| - mainBuffer.write(';');
|
| - }
|
| - }
|
| - if (!mangledGlobalFieldNames.isEmpty) {
|
| - var keys = mangledGlobalFieldNames.keys.toList();
|
| - keys.sort();
|
| - var properties = [];
|
| - for (String key in keys) {
|
| - var value = js.string('${mangledGlobalFieldNames[key]}');
|
| - properties.add(new jsAst.Property(js.string(key), value));
|
| - }
|
| - jsAst.Expression mangledGlobalNamesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.MANGLED_GLOBAL_NAMES);
|
| - var map = new jsAst.ObjectInitializer(properties);
|
| - mainBuffer.write(
|
| - jsAst.prettyPrint(
|
| - js.statement('# = #', [mangledGlobalNamesAccess, map]),
|
| - compiler,
|
| - monitor: compiler.dumpInfoTask));
|
| - if (compiler.enableMinification) {
|
| - mainBuffer.write(';');
|
| - }
|
| - }
|
| - }
|
| -
|
| - void checkEverythingEmitted(Iterable<Element> elements) {
|
| - List<Element> pendingStatics;
|
| - if (!compiler.hasIncrementalSupport) {
|
| - pendingStatics =
|
| - Elements.sortedByPosition(elements.where((e) => !e.isLibrary));
|
| -
|
| - pendingStatics.forEach((element) =>
|
| - compiler.reportInfo(
|
| - element, MessageKind.GENERIC, {'text': 'Pending statics.'}));
|
| - }
|
| -
|
| - if (pendingStatics != null && !pendingStatics.isEmpty) {
|
| - compiler.internalError(pendingStatics.first,
|
| - 'Pending statics (see above).');
|
| - }
|
| - }
|
| -
|
| - void emitMainOutputUnit(Map<OutputUnit, String> deferredLoadHashes,
|
| - CodeBuffer nativeBuffer) {
|
| - bool isProgramSplit = compiler.deferredLoadTask.isProgramSplit;
|
| - OutputUnit mainOutputUnit = compiler.deferredLoadTask.mainOutputUnit;
|
| -
|
| - mainBuffer.add(buildGeneratedBy());
|
| - addComment(HOOKS_API_USAGE, mainBuffer);
|
| -
|
| - if (isProgramSplit) {
|
| - /// For deferred loading we communicate the initializers via this global
|
| - /// variable. The deferred hunks will add their initialization to this.
|
| - /// The semicolon is important in minified mode, without it the
|
| - /// following parenthesis looks like a call to the object literal.
|
| - mainBuffer..add(
|
| - 'self.${deferredInitializers} = self.${deferredInitializers} || '
|
| - 'Object.create(null);$n');
|
| - }
|
| -
|
| - // Using a named function here produces easier to read stack traces in
|
| - // Chrome/V8.
|
| - mainBuffer.add('(function(${namer.currentIsolate})$_{\n');
|
| - if (compiler.hasIncrementalSupport) {
|
| - mainBuffer.add(
|
| - '(this.\$dart_unsafe_eval ='
|
| - ' this.\$dart_unsafe_eval || Object.create(null))'
|
| - '.patch = function(a) { eval(a) }$N');
|
| - }
|
| - if (isProgramSplit) {
|
| - /// We collect all the global state of the, so it can be passed to the
|
| - /// initializer of deferred files.
|
| - mainBuffer.add('var ${globalsHolder}$_=${_}Object.create(null)$N');
|
| - }
|
| - mainBuffer.add('function dart()$_{$n'
|
| - '${_}${_}this.x$_=${_}0$N'
|
| - '${_}${_}delete this.x$N'
|
| - '}$n');
|
| - for (String globalObject in Namer.reservedGlobalObjectNames) {
|
| - // The global objects start as so-called "slow objects". For V8, this
|
| - // means that it won't try to make map transitions as we add properties
|
| - // to these objects. Later on, we attempt to turn these objects into
|
| - // fast objects by calling "convertToFastObject" (see
|
| - // [emitConvertToFastObjectFunction]).
|
| - mainBuffer.write('var ${globalObject}$_=${_}');
|
| - if(isProgramSplit) {
|
| - mainBuffer.add('${globalsHolder}.$globalObject$_=${_}');
|
| - }
|
| - mainBuffer.write('new dart$N');
|
| - }
|
| -
|
| - mainBuffer.add('function ${namer.isolateName}()$_{}\n');
|
| - if (isProgramSplit) {
|
| - mainBuffer
|
| - .write('${globalsHolder}.${namer.isolateName}$_=$_'
|
| - '${namer.isolateName}$N'
|
| - '${globalsHolder}.$initName$_=${_}$initName$N');
|
| - }
|
| - mainBuffer.add('init()$N$n');
|
| - mainBuffer.add('$isolateProperties$_=$_$isolatePropertiesName$N');
|
| -
|
| - emitStaticFunctions(task.outputStaticLists[mainOutputUnit]);
|
| -
|
| - // Only output the classesCollector if we actually have any classes.
|
| - if (!(nativeClasses.isEmpty &&
|
| - compiler.codegenWorld.staticFunctionsNeedingGetter.isEmpty &&
|
| - outputClassLists.values.every((classList) => classList.isEmpty) &&
|
| - typedefsNeededForReflection.isEmpty)) {
|
| - // Shorten the code by using "$$" as temporary.
|
| - classesCollector = r"$$";
|
| - mainBuffer.add('var $classesCollector$_=${_}Object.create(null)$N$n');
|
| - }
|
| -
|
| - if (!nativeClasses.isEmpty) {
|
| - addComment('Native classes', mainBuffer);
|
| - }
|
| -
|
| - List<ClassElement> classes = task.outputClassLists[mainOutputUnit];
|
| - if (classes != null) {
|
| - for (ClassElement element in classes) {
|
| - generateClass(element, getElementDescriptor(element));
|
| - }
|
| - }
|
| -
|
| - if (compiler.enableMinification) {
|
| - mainBuffer.write(';');
|
| - }
|
| -
|
| - if (elementDescriptors.isNotEmpty) {
|
| - Iterable<LibraryElement> libraries =
|
| - task.outputLibraryLists[mainOutputUnit];
|
| - if (libraries == null) libraries = [];
|
| - emitLibraries(libraries);
|
| - emitTypedefs();
|
| - emitMangledNames();
|
| -
|
| - checkEverythingEmitted(elementDescriptors.keys);
|
| -
|
| - CodeBuffer libraryBuffer = new CodeBuffer();
|
| - for (LibraryElement library in Elements.sortedByPosition(libraries)) {
|
| - writeLibraryDescriptors(libraryBuffer, library);
|
| - elementDescriptors.remove(library);
|
| - }
|
| -
|
| - mainBuffer
|
| - ..write('(')
|
| - ..write(
|
| - jsAst.prettyPrint(
|
| - getReflectionDataParser(classesCollector, backend),
|
| - compiler))
|
| - ..write(')')
|
| - ..write('([$n')
|
| - ..add(libraryBuffer)
|
| - ..write('])$N');
|
| -
|
| - emitFinishClassesInvocationIfNecessary(mainBuffer);
|
| - }
|
| -
|
| - typeTestEmitter.emitRuntimeTypeSupport(mainBuffer, mainOutputUnit);
|
| - interceptorEmitter.emitGetInterceptorMethods(mainBuffer);
|
| - interceptorEmitter.emitOneShotInterceptors(mainBuffer);
|
| -
|
| - if (task.outputContainsConstantList) {
|
| - emitMakeConstantList(mainBuffer);
|
| - }
|
| -
|
| - // Constants in checked mode call into RTI code to set type information
|
| - // which may need getInterceptor (and one-shot interceptor) methods, so
|
| - // we have to make sure that [emitGetInterceptorMethods] and
|
| - // [emitOneShotInterceptors] have been called.
|
| - emitCompileTimeConstants(mainBuffer, mainOutputUnit);
|
| -
|
| - emitDeferredBoilerPlate(mainBuffer, deferredLoadHashes);
|
| -
|
| - // Static field initializations require the classes and compile-time
|
| - // constants to be set up.
|
| - emitStaticNonFinalFieldInitializations(mainBuffer);
|
| - interceptorEmitter.emitInterceptedNames(mainBuffer);
|
| - interceptorEmitter.emitMapTypeToInterceptor(mainBuffer);
|
| - emitLazilyInitializedStaticFields(mainBuffer);
|
| -
|
| - mainBuffer.writeln();
|
| - mainBuffer.add(nativeBuffer);
|
| -
|
| - metadataEmitter.emitMetadata(mainBuffer);
|
| -
|
| - isolateProperties = isolatePropertiesName;
|
| - // The following code should not use the short-hand for the
|
| - // initialStatics.
|
| - mainBuffer.add('${namer.currentIsolate}$_=${_}null$N');
|
| -
|
| - emitFinishIsolateConstructorInvocation(mainBuffer);
|
| - mainBuffer.add(
|
| - '${namer.currentIsolate}$_=${_}new ${namer.isolateName}()$N');
|
| -
|
| - emitConvertToFastObjectFunction();
|
| - for (String globalObject in Namer.reservedGlobalObjectNames) {
|
| - mainBuffer.add('$globalObject = convertToFastObject($globalObject)$N');
|
| - }
|
| - if (DEBUG_FAST_OBJECTS) {
|
| - mainBuffer.add(r'''
|
| - // The following only works on V8 when run with option
|
| - // "--allow-natives-syntax". We use'new Function' because the
|
| - // miniparser does not understand V8 native syntax.
|
| - if (typeof print === "function") {
|
| - var HasFastProperties =
|
| - new Function("a", "return %HasFastProperties(a)");
|
| - print("Size of global helper object: "
|
| - + String(Object.getOwnPropertyNames(H).length)
|
| - + ", fast properties " + HasFastProperties(H));
|
| - print("Size of global platform object: "
|
| - + String(Object.getOwnPropertyNames(P).length)
|
| - + ", fast properties " + HasFastProperties(P));
|
| - print("Size of global dart:html object: "
|
| - + String(Object.getOwnPropertyNames(W).length)
|
| - + ", fast properties " + HasFastProperties(W));
|
| - print("Size of isolate properties object: "
|
| - + String(Object.getOwnPropertyNames($).length)
|
| - + ", fast properties " + HasFastProperties($));
|
| - print("Size of constant object: "
|
| - + String(Object.getOwnPropertyNames(C).length)
|
| - + ", fast properties " + HasFastProperties(C));
|
| - var names = Object.getOwnPropertyNames($);
|
| - for (var i = 0; i < names.length; i++) {
|
| - print("$." + names[i]);
|
| - }
|
| - }
|
| -''');
|
| - for (String object in Namer.userGlobalObjects) {
|
| - mainBuffer.add('''
|
| - if (typeof print === "function") {
|
| - print("Size of $object: "
|
| - + String(Object.getOwnPropertyNames($object).length)
|
| - + ", fast properties " + HasFastProperties($object));
|
| -}
|
| -''');
|
| - }
|
| - }
|
| -
|
| - jsAst.FunctionDeclaration precompiledFunctionAst =
|
| - buildCspPrecompiledFunctionFor(mainOutputUnit);
|
| - emitInitFunction(mainBuffer);
|
| - emitMain(mainBuffer);
|
| - mainBuffer.add('})()\n');
|
| -
|
| - if (compiler.useContentSecurityPolicy) {
|
| - mainBuffer.write(
|
| - jsAst.prettyPrint(
|
| - precompiledFunctionAst,
|
| - compiler,
|
| - monitor: compiler.dumpInfoTask,
|
| - allowVariableMinification: false).getText());
|
| - }
|
| -
|
| - String assembledCode = mainBuffer.getText();
|
| - if (generateSourceMap) {
|
| - outputSourceMap(assembledCode, mainBuffer, '',
|
| - compiler.sourceMapUri, compiler.outputUri);
|
| - mainBuffer.add(
|
| - generateSourceMapTag(compiler.sourceMapUri, compiler.outputUri));
|
| - assembledCode = mainBuffer.getText();
|
| - }
|
| -
|
| - compiler.outputProvider('', 'js')
|
| - ..add(assembledCode)
|
| - ..close();
|
| - compiler.assembledCode = assembledCode;
|
| -
|
| - if (!compiler.useContentSecurityPolicy) {
|
| - CodeBuffer cspBuffer = new CodeBuffer();
|
| - cspBuffer.add(mainBuffer);
|
| - cspBuffer.write("""
|
| -{
|
| - var message =
|
| - 'Deprecation: Automatic generation of output for Content Security\\n' +
|
| - 'Policy is deprecated and will be removed with the next development\\n' +
|
| - 'release. Use the --csp option to generate CSP restricted output.';
|
| - if (typeof dartPrint == "function") {
|
| - dartPrint(message);
|
| - } else if (typeof console == "object" && typeof console.log == "function") {
|
| - console.log(message);
|
| - } else if (typeof print == "function") {
|
| - print(message);
|
| - }
|
| -}\n""");
|
| -
|
| - cspBuffer.write(
|
| - jsAst.prettyPrint(
|
| - precompiledFunctionAst, compiler,
|
| - allowVariableMinification: false).getText());
|
| -
|
| - compiler.outputProvider('', 'precompiled.js')
|
| - ..add(cspBuffer.getText())
|
| - ..close();
|
| - }
|
| - }
|
| -
|
| - /// Returns a map from OutputUnit to a hash of its content. The hash uniquely
|
| - /// identifies the code of the output-unit. It does not include
|
| - /// boilerplate JS code, like the sourcemap directives or the hash
|
| - /// itself.
|
| - Map<OutputUnit, String> emitDeferredOutputUnits() {
|
| - if (!compiler.deferredLoadTask.isProgramSplit) return const {};
|
| -
|
| - Map<OutputUnit, CodeBuffer> outputBuffers =
|
| - new Map<OutputUnit, CodeBuffer>();
|
| -
|
| - for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) {
|
| - if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) continue;
|
| -
|
| - List<Element> functions = task.outputStaticLists[outputUnit];
|
| - if (functions != null) {
|
| - emitStaticFunctions(functions);
|
| - }
|
| -
|
| - List<ClassElement> classes = task.outputClassLists[outputUnit];
|
| - if (classes != null) {
|
| - for (ClassElement element in classes) {
|
| - generateClass(element, getElementDescriptor(element));
|
| - }
|
| - }
|
| -
|
| - if (elementDescriptors.isNotEmpty) {
|
| - Iterable<LibraryElement> libraries =
|
| - task.outputLibraryLists[outputUnit];
|
| - if (libraries == null) libraries = [];
|
| - emitLibraries(libraries);
|
| -
|
| - CodeBuffer buffer = new CodeBuffer();
|
| - outputBuffers[outputUnit] = buffer;
|
| - for (LibraryElement library in Elements.sortedByPosition(libraries)) {
|
| - writeLibraryDescriptors(buffer, library);
|
| - elementDescriptors.remove(library);
|
| - }
|
| - }
|
| - }
|
| -
|
| - return emitDeferredCode(outputBuffers);
|
| - }
|
| -
|
| - CodeBuffer buildNativesBuffer() {
|
| - // Emit native classes on [nativeBuffer].
|
| - final CodeBuffer nativeBuffer = new CodeBuffer();
|
| -
|
| - if (nativeClasses.isEmpty) return nativeBuffer;
|
| -
|
| -
|
| - addComment('Native classes', nativeBuffer);
|
| -
|
| - nativeEmitter.generateNativeClasses(nativeClasses, mainBuffer,
|
| - additionalProperties);
|
| -
|
| - nativeEmitter.finishGenerateNativeClasses();
|
| - nativeEmitter.assembleCode(nativeBuffer);
|
| -
|
| - return nativeBuffer;
|
| - }
|
| -
|
| - void emitProgram(Program program) {
|
| - // Shorten the code by using [namer.currentIsolate] as temporary.
|
| - isolateProperties = namer.currentIsolate;
|
| -
|
| - classesCollector = r"$$";
|
| -
|
| - // Emit deferred units first, so we have their hashes.
|
| - // Map from OutputUnit to a hash of its content. The hash uniquely
|
| - // identifies the code of the output-unit. It does not include
|
| - // boilerplate JS code, like the sourcemap directives or the hash
|
| - // itself.
|
| - Map<OutputUnit, String> deferredLoadHashes = emitDeferredOutputUnits();
|
| - CodeBuffer nativeBuffer = buildNativesBuffer();
|
| - emitMainOutputUnit(deferredLoadHashes, nativeBuffer);
|
| -
|
| - if (backend.requiresPreamble &&
|
| - !backend.htmlLibraryIsLoaded) {
|
| - compiler.reportHint(NO_LOCATION_SPANNABLE, MessageKind.PREAMBLE);
|
| - }
|
| - }
|
| -
|
| - String generateSourceMapTag(Uri sourceMapUri, Uri fileUri) {
|
| - if (sourceMapUri != null && fileUri != null) {
|
| - String sourceMapFileName = relativize(fileUri, sourceMapUri, false);
|
| - return '''
|
| -
|
| -//# sourceMappingURL=$sourceMapFileName
|
| -''';
|
| - }
|
| - return '';
|
| - }
|
| -
|
| - ClassBuilder getElementDescriptor(Element element) {
|
| - Element owner = element.library;
|
| - if (!element.isLibrary && !element.isTopLevel && !element.isNative) {
|
| - // For static (not top level) elements, record their code in a buffer
|
| - // specific to the class. For now, not supported for native classes and
|
| - // native elements.
|
| - ClassElement cls =
|
| - element.enclosingClassOrCompilationUnit.declaration;
|
| - if (compiler.codegenWorld.directlyInstantiatedClasses.contains(cls)
|
| - && !cls.isNative) {
|
| - owner = cls;
|
| - }
|
| - }
|
| - if (owner == null) {
|
| - compiler.internalError(element, 'Owner is null.');
|
| - }
|
| - return elementDescriptors.putIfAbsent(
|
| - owner,
|
| - () => new ClassBuilder(owner, namer));
|
| - }
|
| -
|
| - /// Emits support-code for deferred loading into [buffer].
|
| - void emitDeferredBoilerPlate(CodeBuffer buffer,
|
| - Map<OutputUnit, String> deferredLoadHashes) {
|
| - // Function for checking if a hunk is loaded given its hash.
|
| - buffer.write(jsAst.prettyPrint(
|
| - js('# = function(hunkHash) {'
|
| - ' return !!$deferredInitializers[hunkHash];'
|
| - '}', generateEmbeddedGlobalAccess(embeddedNames.IS_HUNK_LOADED)),
|
| - compiler, monitor: compiler.dumpInfoTask));
|
| - buffer.write('$N');
|
| - // Function for initializing a loaded hunk, given its hash.
|
| - buffer.write(jsAst.prettyPrint(
|
| - js('# = function(hunkHash) {'
|
| - ' $deferredInitializers[hunkHash]('
|
| - '$globalsHolder, ${namer.currentIsolate})'
|
| - '}',
|
| - generateEmbeddedGlobalAccess(
|
| - embeddedNames.INITIALIZE_LOADED_HUNK)),
|
| - compiler, monitor: compiler.dumpInfoTask));
|
| - buffer.write('$N');
|
| - // Write a javascript mapping from Deferred import load ids (derrived
|
| - // from the import prefix.) to a list of lists of uris of hunks to load,
|
| - // and a corresponding mapping to a list of hashes used by
|
| - // INITIALIZE_LOADED_HUNK and IS_HUNK_LOADED.
|
| - Map<String, List<String>> deferredLibraryUris =
|
| - new Map<String, List<String>>();
|
| - Map<String, List<String>> deferredLibraryHashes =
|
| - new Map<String, List<String>>();
|
| - compiler.deferredLoadTask.hunksToLoad.forEach(
|
| - (String loadId, List<OutputUnit>outputUnits) {
|
| - List<String> uris = new List<String>();
|
| - List<String> hashes = new List<String>();
|
| - deferredLibraryHashes[loadId] = new List<String>();
|
| - for (OutputUnit outputUnit in outputUnits) {
|
| - uris.add(deferredPartFileName(outputUnit));
|
| - hashes.add(deferredLoadHashes[outputUnit]);
|
| - }
|
| -
|
| - deferredLibraryUris[loadId] = uris;
|
| - deferredLibraryHashes[loadId] = hashes;
|
| - });
|
| -
|
| - void emitMapping(String name, Map<String, List<String>> mapping) {
|
| - List<jsAst.Property> properties = new List<jsAst.Property>();
|
| - mapping.forEach((String key, List<String> values) {
|
| - properties.add(new jsAst.Property(js.escapedString(key),
|
| - new jsAst.ArrayInitializer.from(
|
| - values.map(js.escapedString))));
|
| - });
|
| - jsAst.Node initializer =
|
| - new jsAst.ObjectInitializer(properties, isOneLiner: true);
|
| -
|
| - jsAst.Node globalName = generateEmbeddedGlobalAccess(name);
|
| - buffer.write(jsAst.prettyPrint(
|
| - js("# = #", [globalName, initializer]),
|
| - compiler, monitor: compiler.dumpInfoTask));
|
| - buffer.write('$N');
|
| -
|
| - }
|
| -
|
| - emitMapping(embeddedNames.DEFERRED_LIBRARY_URIS, deferredLibraryUris);
|
| - emitMapping(embeddedNames.DEFERRED_LIBRARY_HASHES,
|
| - deferredLibraryHashes);
|
| - }
|
| -
|
| - /// Emits code for all output units except the main.
|
| - /// Returns a mapping from outputUnit to a hash of the corresponding hunk that
|
| - /// can be used for calling the initializer.
|
| - Map<OutputUnit, String> emitDeferredCode(
|
| - Map<OutputUnit, CodeBuffer> deferredBuffers) {
|
| -
|
| - Map<OutputUnit, String> hunkHashes = new Map<OutputUnit, String>();
|
| -
|
| - for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) {
|
| - if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) continue;
|
| -
|
| - CodeBuffer libraryDescriptorBuffer = deferredBuffers[outputUnit];
|
| -
|
| - CodeBuffer outputBuffer = new CodeBuffer();
|
| -
|
| - outputBuffer..write(buildGeneratedBy())
|
| - ..write('${deferredInitializers}.current$_=$_'
|
| - 'function$_(${globalsHolder}) {$N');
|
| - for (String globalObject in Namer.reservedGlobalObjectNames) {
|
| - outputBuffer
|
| - .write('var $globalObject$_=$_'
|
| - '${globalsHolder}.$globalObject$N');
|
| - }
|
| - outputBuffer
|
| - ..write('var init$_=$_${globalsHolder}.init$N')
|
| - ..write('var ${namer.isolateName}$_=$_'
|
| - '${globalsHolder}.${namer.isolateName}$N');
|
| - if (libraryDescriptorBuffer != null) {
|
| - // TODO(ahe): This defines a lot of properties on the
|
| - // Isolate.prototype object. We know this will turn it into a
|
| - // slow object in V8, so instead we should do something similar
|
| - // to Isolate.$finishIsolateConstructor.
|
| - outputBuffer
|
| - ..write('var ${namer.currentIsolate}$_=$_$isolatePropertiesName$N')
|
| - // The classesCollector object ($$).
|
| - ..write('$classesCollector$_=${_}Object.create(null);$n')
|
| - ..write('(')
|
| - ..write(
|
| - jsAst.prettyPrint(
|
| - getReflectionDataParser(classesCollector, backend),
|
| - compiler, monitor: compiler.dumpInfoTask))
|
| - ..write(')')
|
| - ..write('([$n')
|
| - ..addBuffer(libraryDescriptorBuffer)
|
| - ..write('])$N');
|
| -
|
| - if (outputClassLists.containsKey(outputUnit)) {
|
| - outputBuffer.write(
|
| - '$finishClassesName($classesCollector,$_${namer.currentIsolate},'
|
| - '$_$isolatePropertiesName)$N');
|
| - }
|
| -
|
| - }
|
| -
|
| - // Set the currentIsolate variable to the current isolate (which is
|
| - // provided as second argument).
|
| - // We need to do this, because we use the same variable for setting up
|
| - // the isolate-properties and for storing the current isolate. During
|
| - // the setup (the code above this lines) we must set the variable to
|
| - // the isolate-properties.
|
| - // After we have done the setup (finishing with `finishClasses`) it must
|
| - // point to the current Isolate. Otherwise all methods/functions
|
| - // accessing isolate variables will access the wrong object.
|
| - outputBuffer.write("${namer.currentIsolate}$_=${_}arguments[1]$N");
|
| - typeTestEmitter.emitRuntimeTypeSupport(outputBuffer, outputUnit);
|
| -
|
| - emitCompileTimeConstants(outputBuffer, outputUnit);
|
| - outputBuffer.write('}$N');
|
| -
|
| - if (compiler.useContentSecurityPolicy) {
|
| - jsAst.FunctionDeclaration precompiledFunctionAst =
|
| - buildCspPrecompiledFunctionFor(outputUnit);
|
| -
|
| - outputBuffer.write(
|
| - jsAst.prettyPrint(
|
| - precompiledFunctionAst, compiler,
|
| - monitor: compiler.dumpInfoTask,
|
| - allowVariableMinification: false).getText());
|
| - }
|
| -
|
| - // Make a unique hash of the code (before the sourcemaps are added)
|
| - // This will be used to retrieve the initializing function from the global
|
| - // variable.
|
| - String hash = hashOfString(outputBuffer.getText());
|
| -
|
| - outputBuffer.add('${deferredInitializers}["$hash"]$_=$_'
|
| - '${deferredInitializers}.current$N');
|
| -
|
| - String partPrefix = deferredPartFileName(outputUnit, addExtension: false);
|
| - if (generateSourceMap) {
|
| - Uri mapUri, partUri;
|
| - Uri sourceMapUri = compiler.sourceMapUri;
|
| - Uri outputUri = compiler.outputUri;
|
| -
|
| - String partName = "$partPrefix.part";
|
| -
|
| - if (sourceMapUri != null) {
|
| - String mapFileName = partName + ".js.map";
|
| - List<String> mapSegments = sourceMapUri.pathSegments.toList();
|
| - mapSegments[mapSegments.length - 1] = mapFileName;
|
| - mapUri = compiler.sourceMapUri.replace(pathSegments: mapSegments);
|
| - }
|
| -
|
| - if (outputUri != null) {
|
| - String partFileName = partName + ".js";
|
| - List<String> partSegments = outputUri.pathSegments.toList();
|
| - partSegments[partSegments.length - 1] = partFileName;
|
| - partUri = compiler.outputUri.replace(pathSegments: partSegments);
|
| - }
|
| -
|
| - outputSourceMap(outputBuffer.getText(), outputBuffer, partName,
|
| - mapUri, partUri);
|
| - outputBuffer.add(generateSourceMapTag(mapUri, partUri));
|
| - }
|
| -
|
| - outputBuffers[outputUnit] = outputBuffer;
|
| - compiler.outputProvider(partPrefix, 'part.js')
|
| - ..add(outputBuffer.getText())
|
| - ..close();
|
| -
|
| - hunkHashes[outputUnit] = hash;
|
| - }
|
| - return hunkHashes;
|
| - }
|
| -
|
| - String buildGeneratedBy() {
|
| - var suffix = '';
|
| - if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
|
| - return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n';
|
| - }
|
| -
|
| - void outputSourceMap(String code, CodeBuffer buffer, String name,
|
| - [Uri sourceMapUri, Uri fileUri]) {
|
| - if (!generateSourceMap) return;
|
| - // Create a source file for the compilation output. This allows using
|
| - // [:getLine:] to transform offsets to line numbers in [SourceMapBuilder].
|
| - SourceFile compiledFile = new StringSourceFile(null, code);
|
| - SourceMapBuilder sourceMapBuilder =
|
| - new SourceMapBuilder(sourceMapUri, fileUri, compiledFile);
|
| - buffer.forEachSourceLocation(sourceMapBuilder.addMapping);
|
| - String sourceMap = sourceMapBuilder.build();
|
| - compiler.outputProvider(name, 'js.map')
|
| - ..add(sourceMap)
|
| - ..close();
|
| - }
|
| -
|
| - void invalidateCaches() {
|
| - if (!compiler.hasIncrementalSupport) return;
|
| - if (cachedElements.isEmpty) return;
|
| - for (Element element in compiler.enqueuer.codegen.newlyEnqueuedElements) {
|
| - if (element.isInstanceMember) {
|
| - cachedClassBuilders.remove(element.enclosingClass);
|
| -
|
| - nativeEmitter.cachedBuilders.remove(element.enclosingClass);
|
| -
|
| - }
|
| - }
|
| - }
|
| -}
|
|
|