Chromium Code Reviews| Index: pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart |
| diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart |
| index 2e271d0b2a5d33e006e8e9aa21a7c29479a91d69..390ecd684ea42eb5ace5a8127e5759c023f5294b 100644 |
| --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart |
| +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart |
| @@ -4,6 +4,241 @@ |
| part of dart2js.js_emitter.startup_emitter.model_emitter; |
| +/// The fast startup emitter's goal is to minimize the amount of work that the |
| +/// JavaScript engine has to do before it can start running user code. |
| +/// |
| +/// Whenever possible, the emitter uses object literals instead of updating |
| +/// objects. |
| +/// |
| +/// Example: |
| +/// |
| +/// // Holders are initialized directly with the classes and static |
| +/// // functions. |
| +/// var A = { Point: function Point(x, y) { this.x = x; this.y = y }, |
| +/// someStaticFunction: function someStaticFunction() { ... } }; |
| +/// |
| +/// // Class-behavior is emitted in a prototype object that is directly |
| +/// // assigned: |
| +/// A.Point.prototype = { distanceTo: function(other) { ... } }; |
| +/// |
| +/// // Inheritance is achieved by updating the prototype objects (hidden in |
| +/// // a helper function): |
| +/// A.Point.prototype.__proto__ = H.Object.prototype; |
| +/// |
| +/// The emitter doesn't try to be clever and emits everything beforehand. This |
| +/// increases the output size, but improves performance. |
| +/// |
| +// The code relies on the fact that all Dart code is inside holders. As such |
| +// we can use "global" names however we want. As long as we don't shadow |
| +// JavaScript variables (like `Array`) we are free to chose whatever variable |
| +// names we want. Furthermore, the minifier reduces the names of the variables. |
| +const String mainBoilerplate = ''' |
|
Siggi Cherem (dart-lang)
2015/07/14 15:57:25
minor nit question: Since you have other changes s
floitsch
2015/07/17 17:59:32
I found this to be the most important part of the
|
| +{ |
| +// Declare deferred-initializer global, which is used to keep track of the |
| +// loaded fragments. |
| +#deferredInitializer; |
| + |
| +(function() { |
| +// Copies the own properties from [from] to [to]. |
| +function copyProperties(from, to) { |
| + var keys = Object.keys(from); |
| + for (var i = 0; i < keys.length; i++) { |
| + to[keys[i]] = from[keys[i]]; |
| + } |
| +} |
| + |
| +// Makes [cls] inherit from [sup]. |
| +// On Chrome, Firefox and recent IEs this happens by updating the internal |
| +// proto-property of the classes 'prototype' field. |
| +// Older IEs use `Object.create` and copy over the properties. |
| +function inherit(cls, sup) { |
| + // TODO(floitsch): IE doesn't support changing the __proto__ property. There, |
| + // we need to copy the properties instead. |
| + cls.#typeNameProperty = cls.name; // Needed for RTI. |
| + cls.prototype.constructor = cls; |
| + cls.prototype[#operatorIsPrefix + cls.name] = cls; |
| + cls.prototype.__proto__ = sup.prototype; |
| +} |
| + |
| +// Mixes in the properties of [mixin] into [cls]. |
| +function mixin(cls, mixin) { |
| + copyProperties(mixin.prototype, cls.prototype); |
| +} |
| + |
| +// Creates a lazy field. |
| +// |
| +// A lazy field has a storage entry, [name], which holds the value, and a |
| +// getter ([getterName]) to access the field. If the field wasn't set before |
| +// the first access, it is initialized with the [initializer]. |
| +function lazy(holder, name, getterName, initializer) { |
| + holder[name] = null; |
| + holder[getterName] = function() { |
| + holder[getterName] = function() { #cyclicThrow(name) }; |
| + var result; |
| + var sentinelInProgress = initializer; |
| + 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; |
| + } |
| + // TODO(floitsch): for performance reasons the function should probably |
| + // be unique for each static. |
| + holder[getterName] = function() { return this[name]; }; |
| + } |
| + return result; |
| + }; |
| +} |
| + |
| +// Given a list, marks it as constant. |
| +// |
| +// The runtime ensures that const-lists cannot be modified. |
| +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; |
| +} |
| + |
| +// TODO(floitsch): provide code for tear-offs. |
| + |
| +// Instead of setting the interceptor tags directly we use this update |
| +// function. This makes it easier for deferred fragments to contribute to the |
| +// embedded global. |
| +function setOrUpdateInterceptorsByTag(newTags) { |
| + var tags = #embeddedInterceptorTags; |
| + if (!tags) { |
| + #embeddedInterceptorTags = newTags; |
| + return; |
| + } |
| + copyProperties(newTags, tags); |
| +} |
| + |
| +// Instead of setting the leaf tags directly we use this update |
| +// function. This makes it easier for deferred fragments to contribute to the |
| +// embedded global. |
| +function setOrUpdateLeafTags(newTags) { |
| + var tags = #embeddedLeafTags; |
| + if (!tags) { |
| + #embeddedLeafTags = newTags; |
| + return; |
| + } |
| + copyProperties(newTags, tags); |
| +} |
| + |
| +// Updates the types embedded global. |
| +function updateTypes(newTypes) { |
| + var types = #embeddedTypes; |
| + types.push.apply(types, newTypes); |
| +} |
| + |
| +// Updates the given holder with the properties of the [newHolder]. |
| +// This function is used when a deferred fragment is initialized. |
| +function updateHolder(holder, newHolder) { |
| + // TODO(floitsch): updating the prototype (instead of copying) is |
| + // *horribly* inefficient in Firefox. There we should just copy the |
| + // properties. |
| + var oldPrototype = holder.__proto__; |
| + newHolder.__proto__ = oldPrototype; |
| + holder.__proto__ = newHolder; |
| + return holder; |
| +} |
| + |
| +// Every deferred hunk (i.e. fragment) is a function that we can invoke to |
| +// initialize it. At this moment it contributes its data to the main hunk. |
| +function initializeDeferredHunk(hunk) { |
| + // TODO(floitsch): extend natives. |
| + hunk(derive, mixin, lazy, makeConstList, installTearOff, |
| + updateHolder, updateTypes, updateInterceptorsByTag, updateLeafTags, |
| + #embeddedGlobalsObject, #holdersList, #currentIsolate); |
| +} |
| + |
| +// Creates the holders. |
| +#holders; |
| +// Sets the prototypes of classes. |
| +#prototypes; |
| +// Sets aliases of methods (on the prototypes of classes). |
| +#aliases; |
| +// Installs the tear-offs of functions. |
| +#tearOffs; |
| +// Builds the inheritance structure. |
| +#inheritance; |
| + |
| +// Emits the embedded globals. |
| +#embeddedGlobals; |
| + |
| +// Sets up the native support. |
| +// Native-support uses setOrUpdateInterceptorsByTag and setOrUpdateLeafTags. |
| +#nativeSupport; |
| + |
| +// Instantiates all constants. |
| +#constants; |
| +// Initializes the static non-final fields (with their constant values). |
| +#staticNonFinalFields; |
| +// Creates lazy getters for statics that must run initializers on first access. |
| +#lazyStatics; |
| + |
| +// Invokes main (making sure that it records the 'current-script' value). |
| +#invokeMain; |
| +})(); |
| +}'''; |
| + |
| +/// Deferred fragments (aka 'hunks') are built similarly to the main fragment. |
| +/// |
| +/// However, at specific moments they need to contribute their data. |
| +/// For example, once the holders have been created, they are included into |
| +/// the main holders. |
| +const String deferredBoilerplate = ''' |
| +{ |
| +#deferredInitializers.current = |
| +function(derive, mixin, lazy, makeConstList, installTearOff, |
| + updateHolder, updateTypes, |
| + setOrUpdateInterceptorsByTag, setOrUpdateLeafTags, |
| + #embeddedGlobalsObject, holdersList, #currentIsolate) { |
| + |
| +// Builds the holders. They only contain the data for new holders. |
| +#holders; |
| +// Updates the holders of the main-fragment. Uses the provided holdersList to |
| +// access the main holders. |
| +// The local holders are replaced by the combined holders. This is necessary |
| +// for the inheritance setup below. |
| +#updateHolders; |
| +// Sets the prototypes of the new classes. |
| +#prototypes; |
| +// Sets aliases of methods (on the prototypes of classes). |
| +#aliases; |
| +// Installs the tear-offs of functions. |
| +#tearOffs; |
| +// Builds the inheritance structure. |
| +#inheritance; |
| + |
| +updateTypes(#types); |
| + |
| +// Native-support uses setOrUpdateInterceptorsByTag and setOrUpdateLeafTags. |
| +#nativeSupport; |
| + |
| +// Instantiates all constants of this deferred fragment. |
| +// Note that the constant-holder has been updated earlier and storing the |
| +// constant values in the constant-holder makes them available globally. |
| +#constants; |
| +// Initializes the static non-final fields (with their constant values). |
| +#staticNonFinalFields; |
| +// Creates lazy getters for statics that must run initializers on first access. |
| +#lazyStatics; |
| +}; |
| +// TODO(floitsch): this last line should be outside the AST, since it |
| +// requires to know the hash of the part of the code above this comment. |
| +#deferredInitializers[#hash] = #deferredInitializers.current; |
| +}'''; |
| + |
| /** |
| * This class builds a JavaScript tree for a given fragment. |
| * |