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.startup_js_emitter.model_emitter; | 5 part of dart2js.startup_js_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. | |
|
herhut
2015/07/27 11:02:22
To be uber precise here, it is the pretty printer
floitsch
2015/07/29 15:26:24
Updated the comment (new CL coming).
Since the hol
| |
| 35 const String mainBoilerplate = ''' | |
| 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 constructor.#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 initialize with the [initializer]. | |
|
Siggi Cherem (dart-lang)
2015/07/09 23:54:07
nit: it is initialize => it is initialized
floitsch
2015/07/10 17:13:59
Done.
| |
| 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 // This variable is used by the tearOffCode to guarantee unique functions per | |
| 112 // tear-offs. | |
| 113 var functionCounter = 0; | |
| 114 #tearOffCode; | |
| 115 | |
| 116 // Adapts the stored data, so it's suitable for a tearOff call. | |
| 117 // TODO(floitsch): Change tearOffCode to accept the data directly, or create a | |
| 118 // different tearOffCode? | |
| 119 function installTearOff(container, getterName, | |
| 120 isStatic, isIntercepted, requiredParameterCount, | |
| 121 optionalParameterDefaultValues, | |
| 122 callNames, funNames, funType) { | |
| 123 var funs = []; | |
| 124 for (var i = 0; i < funNames.length; i++) { | |
| 125 var fun = container[funNames[i]]; | |
| 126 fun.#callName = callNames[i]; | |
| 127 funs.push(fun); | |
| 128 } | |
| 129 | |
| 130 funs[0][#argumentCount] = requiredParameterCount; | |
| 131 funs[0][#defaultArgumentValues] = optionalParameterDefaultValues; | |
| 132 var reflectionInfo = funType; | |
| 133 var name = funNames[0]; | |
| 134 container[getterName] = | |
| 135 tearOff(funs, reflectionInfo, isStatic, name, isIntercepted); | |
| 136 } | |
| 137 | |
| 138 // Instead of setting the interceptor tags directly we use this update | |
| 139 // function. This makes it easier for deferred fragments to contribute to the | |
| 140 // embedded global. | |
| 141 function setOrUpdateInterceptorsByTag(newTags) { | |
| 142 var tags = #embeddedInterceptorTags; | |
| 143 if (!tags) { | |
| 144 #embeddedInterceptorTags = newTags; | |
| 145 return; | |
| 146 } | |
| 147 copyProperties(newTags, tags); | |
| 148 } | |
| 149 | |
| 150 // Instead of setting the leaf tags directly we use this update | |
| 151 // function. This makes it easier for deferred fragments to contribute to the | |
| 152 // embedded global. | |
| 153 function setOrUpdateLeafTags(newTags) { | |
| 154 var tags = #embeddedLeafTags; | |
| 155 if (!tags) { | |
| 156 #embeddedLeafTags = newTags; | |
| 157 return; | |
| 158 } | |
| 159 copyProperties(newTags, tags); | |
| 160 } | |
| 161 | |
| 162 // Updates the types embedded global. | |
| 163 function updateTypes(newTypes) { | |
| 164 var types = #embeddedTypes; | |
| 165 types.push.apply(types, newTypes); | |
| 166 } | |
| 167 | |
| 168 // Updates the given holder with the properties of the [newHolder]. | |
| 169 // This function is used when a deferred fragment is initialized. | |
| 170 function updateHolder(holder, newHolder) { | |
| 171 // TODO(floitsch): updating the prototype (instead of copying) is | |
| 172 // *horribly* inefficient in Firefox. There we should just copy the | |
| 173 // properties. | |
| 174 var oldPrototype = holder.__proto__; | |
| 175 newHolder.__proto__ = oldPrototype; | |
| 176 holder.__proto__ = newHolder; | |
| 177 return holder; | |
| 178 } | |
| 179 | |
| 180 // Every deferred hunk (i.e. fragment) is a function that we can invoke to | |
| 181 // initialize it. At this moment it contributes its data to the main hunk. | |
| 182 function initializeDeferredHunk(hunk) { | |
| 183 // TODO(floitsch): extend natives. | |
| 184 hunk(derive, mixin, lazy, makeConstList, installTearOff, | |
| 185 updateHolder, updateTypes, updateInterceptorsByTag, updateLeafTags, | |
| 186 #embeddedGlobalsObject, #holdersList, #currentIsolate); | |
| 187 } | |
| 188 | |
| 189 // Creates the holders. | |
| 190 #holders; | |
| 191 // Sets the prototypes of classes. | |
| 192 #prototypes; | |
| 193 // Sets aliases of methods (on the prototypes of classes). | |
| 194 #aliases; | |
| 195 // Installs the tear-offs of functions. | |
| 196 #tearOffs; | |
| 197 // Builds the inheritance structure. | |
| 198 #inheritance; | |
| 199 | |
| 200 // Emits the embedded globals. | |
| 201 #embeddedGlobals; | |
| 202 | |
| 203 // Sets up the native support. | |
| 204 // Native-support uses setOrUpdateInterceptorsByTag and setOrUpdateLeafTags. | |
| 205 #nativeSupport; | |
| 206 | |
| 207 // Instantiates all constants. | |
| 208 #constants; | |
| 209 // Initializes the static non-final fields (with their constant values). | |
| 210 #staticNonFinalFields; | |
| 211 // Creates lazy getters for statics that must run initializers on first access. | |
| 212 #lazyStatics; | |
| 213 | |
| 214 // Invokes main (making sure that it records the 'current-script' value). | |
| 215 #invokeMain; | |
| 216 })(); | |
| 217 }'''; | |
| 218 | |
| 219 /// Deferred fragments (aka 'hunks') are built similarly to the main fragment. | |
| 220 /// | |
| 221 /// However, at specific moments they need to contribute their data. | |
| 222 /// For example, once the holders have been created, they are included into | |
| 223 /// the main holders. | |
| 224 const String deferredBoilerplate = ''' | |
| 225 { | |
| 226 #deferredInitializers.current = | |
| 227 function(derive, mixin, lazy, makeConstList, installTearOff, | |
| 228 updateHolder, updateTypes, | |
| 229 setOrUpdateInterceptorsByTag, setOrUpdateLeafTags, | |
| 230 #embeddedGlobalsObject, holdersList, #currentIsolate) { | |
| 231 | |
| 232 // Builds the holders. They only contain the data for new holders. | |
| 233 #holders; | |
| 234 // Updates the holders of the main-fragment. Uses the provided holdersList to | |
| 235 // access the main holders. | |
| 236 // The local holders are replaced by the combined holders. This is necessary | |
| 237 // for the inheritance setup below. | |
| 238 #updateHolders; | |
| 239 // Sets the prototypes of the new classes. | |
| 240 #prototypes; | |
| 241 // Sets aliases of methods (on the prototypes of classes). | |
| 242 #aliases; | |
| 243 // Installs the tear-offs of functions. | |
| 244 #tearOffs; | |
| 245 // Builds the inheritance structure. | |
| 246 #inheritance; | |
| 247 | |
| 248 updateTypes(#types); | |
| 249 | |
| 250 // Native-support uses setOrUpdateInterceptorsByTag and setOrUpdateLeafTags. | |
| 251 #nativeSupport; | |
| 252 | |
| 253 // Instantiates all constants of this deferred fragment. | |
| 254 // Note that the constant-holder has been updated earlier and storing the | |
| 255 // constant values in the constant-holder makes them available globally. | |
| 256 #constants; | |
| 257 // Initializes the static non-final fields (with their constant values). | |
| 258 #staticNonFinalFields; | |
| 259 // Creates lazy getters for statics that must run initializers on first access. | |
| 260 #lazyStatics; | |
| 261 }; | |
| 262 // TODO(floitsch): this last line should be outside the AST, since it | |
| 263 // requires to know the hash of the part of the code above this comment. | |
| 264 #deferredInitializers[#hash] = #deferredInitializers.current; | |
| 265 }'''; | |
| 266 | |
| 7 /** | 267 /** |
| 8 * This class builds a JavaScript tree for a given fragment. | 268 * This class builds a JavaScript tree for a given fragment. |
| 9 * | 269 * |
| 10 * A fragment is generally written into a separate file so that it can be | 270 * A fragment is generally written into a separate file so that it can be |
| 11 * loaded dynamically when a deferred library is loaded. | 271 * loaded dynamically when a deferred library is loaded. |
| 12 * | 272 * |
| 13 * This class is stateless and can be reused for different fragments. | 273 * This class is stateless and can be reused for different fragments. |
| 14 */ | 274 */ |
| 15 class FragmentEmitter { | 275 class FragmentEmitter { |
| 16 final Compiler compiler; | 276 final Compiler compiler; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 32 MainFragment fragment = program.fragments.first; | 292 MainFragment fragment = program.fragments.first; |
| 33 throw new UnimplementedError('emitMain'); | 293 throw new UnimplementedError('emitMain'); |
| 34 } | 294 } |
| 35 | 295 |
| 36 js.Statement emitDeferredFragment(DeferredFragment fragment, | 296 js.Statement emitDeferredFragment(DeferredFragment fragment, |
| 37 js.Expression deferredTypes, | 297 js.Expression deferredTypes, |
| 38 List<Holder> holders) { | 298 List<Holder> holders) { |
| 39 throw new UnimplementedError('emitDeferred'); | 299 throw new UnimplementedError('emitDeferred'); |
| 40 } | 300 } |
| 41 } | 301 } |
| OLD | NEW |