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 |