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. |
* |