Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart2js.js_emitter.startup_emitter.model_emitter; | 5 part of dart2js.js_emitter.startup_emitter.model_emitter; |
| 6 | 6 |
| 7 /// The fast startup emitter's goal is to minimize the amount of work that the | |
| 8 /// JavaScript engine has to do before it can start running user code. | |
| 9 /// | |
| 10 /// Whenever possible, the emitter uses object literals instead of updating | |
| 11 /// objects. | |
| 12 /// | |
| 13 /// Example: | |
| 14 /// | |
| 15 /// // Holders are initialized directly with the classes and static | |
| 16 /// // functions. | |
| 17 /// var A = { Point: function Point(x, y) { this.x = x; this.y = y }, | |
| 18 /// someStaticFunction: function someStaticFunction() { ... } }; | |
| 19 /// | |
| 20 /// // Class-behavior is emitted in a prototype object that is directly | |
| 21 /// // assigned: | |
| 22 /// A.Point.prototype = { distanceTo: function(other) { ... } }; | |
| 23 /// | |
| 24 /// // Inheritance is achieved by updating the prototype objects (hidden in | |
| 25 /// // a helper function): | |
| 26 /// A.Point.prototype.__proto__ = H.Object.prototype; | |
| 27 /// | |
| 28 /// The emitter doesn't try to be clever and emits everything beforehand. This | |
| 29 /// increases the output size, but improves performance. | |
| 30 /// | |
| 31 // The code relies on the fact that all Dart code is inside holders. As such | |
| 32 // we can use "global" names however we want. As long as we don't shadow | |
| 33 // JavaScript variables (like `Array`) we are free to chose whatever variable | |
| 34 // names we want. Furthermore, the minifier reduces the names of the variables. | |
| 35 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
| |
| 36 { | |
| 37 // Declare deferred-initializer global, which is used to keep track of the | |
| 38 // loaded fragments. | |
| 39 #deferredInitializer; | |
| 40 | |
| 41 (function() { | |
| 42 // Copies the own properties from [from] to [to]. | |
| 43 function copyProperties(from, to) { | |
| 44 var keys = Object.keys(from); | |
| 45 for (var i = 0; i < keys.length; i++) { | |
| 46 to[keys[i]] = from[keys[i]]; | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 // Makes [cls] inherit from [sup]. | |
| 51 // On Chrome, Firefox and recent IEs this happens by updating the internal | |
| 52 // proto-property of the classes 'prototype' field. | |
| 53 // Older IEs use `Object.create` and copy over the properties. | |
| 54 function inherit(cls, sup) { | |
| 55 // TODO(floitsch): IE doesn't support changing the __proto__ property. There, | |
| 56 // we need to copy the properties instead. | |
| 57 cls.#typeNameProperty = cls.name; // Needed for RTI. | |
| 58 cls.prototype.constructor = cls; | |
| 59 cls.prototype[#operatorIsPrefix + cls.name] = cls; | |
| 60 cls.prototype.__proto__ = sup.prototype; | |
| 61 } | |
| 62 | |
| 63 // Mixes in the properties of [mixin] into [cls]. | |
| 64 function mixin(cls, mixin) { | |
| 65 copyProperties(mixin.prototype, cls.prototype); | |
| 66 } | |
| 67 | |
| 68 // Creates a lazy field. | |
| 69 // | |
| 70 // A lazy field has a storage entry, [name], which holds the value, and a | |
| 71 // getter ([getterName]) to access the field. If the field wasn't set before | |
| 72 // the first access, it is initialized with the [initializer]. | |
| 73 function lazy(holder, name, getterName, initializer) { | |
| 74 holder[name] = null; | |
| 75 holder[getterName] = function() { | |
| 76 holder[getterName] = function() { #cyclicThrow(name) }; | |
| 77 var result; | |
| 78 var sentinelInProgress = initializer; | |
| 79 try { | |
| 80 result = holder[name] = sentinelInProgress; | |
| 81 result = holder[name] = initializer(); | |
| 82 } finally { | |
| 83 // Use try-finally, not try-catch/throw as it destroys the stack | |
| 84 // trace. | |
| 85 if (result === sentinelInProgress) { | |
| 86 // The lazy static (holder[name]) might have been set to a different | |
| 87 // value. According to spec we still have to reset it to null, if | |
| 88 // the initialization failed. | |
| 89 holder[name] = null; | |
| 90 } | |
| 91 // TODO(floitsch): for performance reasons the function should probably | |
| 92 // be unique for each static. | |
| 93 holder[getterName] = function() { return this[name]; }; | |
| 94 } | |
| 95 return result; | |
| 96 }; | |
| 97 } | |
| 98 | |
| 99 // Given a list, marks it as constant. | |
| 100 // | |
| 101 // The runtime ensures that const-lists cannot be modified. | |
| 102 function makeConstList(list) { | |
| 103 // By assigning a function to the properties they become part of the | |
| 104 // hidden class. The actual values of the fields don't matter, since we | |
| 105 // only check if they exist. | |
| 106 list.immutable\$list = Array; | |
| 107 list.fixed\$length = Array; | |
| 108 return list; | |
| 109 } | |
| 110 | |
| 111 // TODO(floitsch): provide code for tear-offs. | |
| 112 | |
| 113 // Instead of setting the interceptor tags directly we use this update | |
| 114 // function. This makes it easier for deferred fragments to contribute to the | |
| 115 // embedded global. | |
| 116 function setOrUpdateInterceptorsByTag(newTags) { | |
| 117 var tags = #embeddedInterceptorTags; | |
| 118 if (!tags) { | |
| 119 #embeddedInterceptorTags = newTags; | |
| 120 return; | |
| 121 } | |
| 122 copyProperties(newTags, tags); | |
| 123 } | |
| 124 | |
| 125 // Instead of setting the leaf tags directly we use this update | |
| 126 // function. This makes it easier for deferred fragments to contribute to the | |
| 127 // embedded global. | |
| 128 function setOrUpdateLeafTags(newTags) { | |
| 129 var tags = #embeddedLeafTags; | |
| 130 if (!tags) { | |
| 131 #embeddedLeafTags = newTags; | |
| 132 return; | |
| 133 } | |
| 134 copyProperties(newTags, tags); | |
| 135 } | |
| 136 | |
| 137 // Updates the types embedded global. | |
| 138 function updateTypes(newTypes) { | |
| 139 var types = #embeddedTypes; | |
| 140 types.push.apply(types, newTypes); | |
| 141 } | |
| 142 | |
| 143 // Updates the given holder with the properties of the [newHolder]. | |
| 144 // This function is used when a deferred fragment is initialized. | |
| 145 function updateHolder(holder, newHolder) { | |
| 146 // TODO(floitsch): updating the prototype (instead of copying) is | |
| 147 // *horribly* inefficient in Firefox. There we should just copy the | |
| 148 // properties. | |
| 149 var oldPrototype = holder.__proto__; | |
| 150 newHolder.__proto__ = oldPrototype; | |
| 151 holder.__proto__ = newHolder; | |
| 152 return holder; | |
| 153 } | |
| 154 | |
| 155 // Every deferred hunk (i.e. fragment) is a function that we can invoke to | |
| 156 // initialize it. At this moment it contributes its data to the main hunk. | |
| 157 function initializeDeferredHunk(hunk) { | |
| 158 // TODO(floitsch): extend natives. | |
| 159 hunk(derive, mixin, lazy, makeConstList, installTearOff, | |
| 160 updateHolder, updateTypes, updateInterceptorsByTag, updateLeafTags, | |
| 161 #embeddedGlobalsObject, #holdersList, #currentIsolate); | |
| 162 } | |
| 163 | |
| 164 // Creates the holders. | |
| 165 #holders; | |
| 166 // Sets the prototypes of classes. | |
| 167 #prototypes; | |
| 168 // Sets aliases of methods (on the prototypes of classes). | |
| 169 #aliases; | |
| 170 // Installs the tear-offs of functions. | |
| 171 #tearOffs; | |
| 172 // Builds the inheritance structure. | |
| 173 #inheritance; | |
| 174 | |
| 175 // Emits the embedded globals. | |
| 176 #embeddedGlobals; | |
| 177 | |
| 178 // Sets up the native support. | |
| 179 // Native-support uses setOrUpdateInterceptorsByTag and setOrUpdateLeafTags. | |
| 180 #nativeSupport; | |
| 181 | |
| 182 // Instantiates all constants. | |
| 183 #constants; | |
| 184 // Initializes the static non-final fields (with their constant values). | |
| 185 #staticNonFinalFields; | |
| 186 // Creates lazy getters for statics that must run initializers on first access. | |
| 187 #lazyStatics; | |
| 188 | |
| 189 // Invokes main (making sure that it records the 'current-script' value). | |
| 190 #invokeMain; | |
| 191 })(); | |
| 192 }'''; | |
| 193 | |
| 194 /// Deferred fragments (aka 'hunks') are built similarly to the main fragment. | |
| 195 /// | |
| 196 /// However, at specific moments they need to contribute their data. | |
| 197 /// For example, once the holders have been created, they are included into | |
| 198 /// the main holders. | |
| 199 const String deferredBoilerplate = ''' | |
| 200 { | |
| 201 #deferredInitializers.current = | |
| 202 function(derive, mixin, lazy, makeConstList, installTearOff, | |
| 203 updateHolder, updateTypes, | |
| 204 setOrUpdateInterceptorsByTag, setOrUpdateLeafTags, | |
| 205 #embeddedGlobalsObject, holdersList, #currentIsolate) { | |
| 206 | |
| 207 // Builds the holders. They only contain the data for new holders. | |
| 208 #holders; | |
| 209 // Updates the holders of the main-fragment. Uses the provided holdersList to | |
| 210 // access the main holders. | |
| 211 // The local holders are replaced by the combined holders. This is necessary | |
| 212 // for the inheritance setup below. | |
| 213 #updateHolders; | |
| 214 // Sets the prototypes of the new classes. | |
| 215 #prototypes; | |
| 216 // Sets aliases of methods (on the prototypes of classes). | |
| 217 #aliases; | |
| 218 // Installs the tear-offs of functions. | |
| 219 #tearOffs; | |
| 220 // Builds the inheritance structure. | |
| 221 #inheritance; | |
| 222 | |
| 223 updateTypes(#types); | |
| 224 | |
| 225 // Native-support uses setOrUpdateInterceptorsByTag and setOrUpdateLeafTags. | |
| 226 #nativeSupport; | |
| 227 | |
| 228 // Instantiates all constants of this deferred fragment. | |
| 229 // Note that the constant-holder has been updated earlier and storing the | |
| 230 // constant values in the constant-holder makes them available globally. | |
| 231 #constants; | |
| 232 // Initializes the static non-final fields (with their constant values). | |
| 233 #staticNonFinalFields; | |
| 234 // Creates lazy getters for statics that must run initializers on first access. | |
| 235 #lazyStatics; | |
| 236 }; | |
| 237 // TODO(floitsch): this last line should be outside the AST, since it | |
| 238 // requires to know the hash of the part of the code above this comment. | |
| 239 #deferredInitializers[#hash] = #deferredInitializers.current; | |
| 240 }'''; | |
| 241 | |
| 7 /** | 242 /** |
| 8 * This class builds a JavaScript tree for a given fragment. | 243 * This class builds a JavaScript tree for a given fragment. |
| 9 * | 244 * |
| 10 * A fragment is generally written into a separate file so that it can be | 245 * A fragment is generally written into a separate file so that it can be |
| 11 * loaded dynamically when a deferred library is loaded. | 246 * loaded dynamically when a deferred library is loaded. |
| 12 * | 247 * |
| 13 * This class is stateless and can be reused for different fragments. | 248 * This class is stateless and can be reused for different fragments. |
| 14 */ | 249 */ |
| 15 class FragmentEmitter { | 250 class FragmentEmitter { |
| 16 final Compiler compiler; | 251 final Compiler compiler; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 32 MainFragment fragment = program.fragments.first; | 267 MainFragment fragment = program.fragments.first; |
| 33 throw new UnimplementedError('emitMain'); | 268 throw new UnimplementedError('emitMain'); |
| 34 } | 269 } |
| 35 | 270 |
| 36 js.Statement emitDeferredFragment(DeferredFragment fragment, | 271 js.Statement emitDeferredFragment(DeferredFragment fragment, |
| 37 js.Expression deferredTypes, | 272 js.Expression deferredTypes, |
| 38 List<Holder> holders) { | 273 List<Holder> holders) { |
| 39 throw new UnimplementedError('emitDeferred'); | 274 throw new UnimplementedError('emitDeferred'); |
| 40 } | 275 } |
| 41 } | 276 } |
| OLD | NEW |