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 |