OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 part of dart2js.js_emitter; | |
6 | |
7 | |
8 class OldEmitter implements Emitter { | |
9 final Compiler compiler; | |
10 final CodeEmitterTask task; | |
11 | |
12 final ContainerBuilder containerBuilder = new ContainerBuilder(); | |
13 final ClassEmitter classEmitter = new ClassEmitter(); | |
14 final NsmEmitter nsmEmitter = new NsmEmitter(); | |
15 TypeTestEmitter get typeTestEmitter => task.typeTestEmitter; | |
16 final InterceptorEmitter interceptorEmitter = new InterceptorEmitter(); | |
17 final MetadataEmitter metadataEmitter = new MetadataEmitter(); | |
18 | |
19 final Set<ConstantValue> cachedEmittedConstants; | |
20 final CodeBuffer cachedEmittedConstantsBuffer = new CodeBuffer(); | |
21 final Map<Element, ClassBuilder> cachedClassBuilders; | |
22 final Set<Element> cachedElements; | |
23 | |
24 bool needsDefineClass = false; | |
25 bool needsMixinSupport = false; | |
26 bool needsLazyInitializer = false; | |
27 final Namer namer; | |
28 ConstantEmitter constantEmitter; | |
29 NativeEmitter get nativeEmitter => task.nativeEmitter; | |
30 | |
31 // The full code that is written to each hunk part-file. | |
32 Map<OutputUnit, CodeBuffer> outputBuffers = new Map<OutputUnit, CodeBuffer>(); | |
33 final CodeBuffer deferredConstants = new CodeBuffer(); | |
34 | |
35 /** Shorter access to [isolatePropertiesName]. Both here in the code, as | |
36 well as in the generated code. */ | |
37 String isolateProperties; | |
38 String classesCollector; | |
39 Set<ClassElement> get neededClasses => task.neededClasses; | |
40 Map<OutputUnit, List<ClassElement>> get outputClassLists | |
41 => task.outputClassLists; | |
42 Map<OutputUnit, List<ConstantValue>> get outputConstantLists | |
43 => task.outputConstantLists; | |
44 List<ClassElement> get nativeClasses => task.nativeClasses; | |
45 final Map<String, String> mangledFieldNames = <String, String>{}; | |
46 final Map<String, String> mangledGlobalFieldNames = <String, String>{}; | |
47 final Set<String> recordedMangledNames = new Set<String>(); | |
48 | |
49 final Map<ClassElement, Map<String, jsAst.Expression>> additionalProperties = | |
50 new Map<ClassElement, Map<String, jsAst.Expression>>(); | |
51 | |
52 List<TypedefElement> get typedefsNeededForReflection => | |
53 task.typedefsNeededForReflection; | |
54 | |
55 JavaScriptBackend get backend => compiler.backend; | |
56 TypeVariableHandler get typeVariableHandler => backend.typeVariableHandler; | |
57 | |
58 String get _ => space; | |
59 String get space => compiler.enableMinification ? "" : " "; | |
60 String get n => compiler.enableMinification ? "" : "\n"; | |
61 String get N => compiler.enableMinification ? "\n" : ";\n"; | |
62 | |
63 CodeBuffer getBuffer(OutputUnit outputUnit) { | |
64 return outputBuffers.putIfAbsent(outputUnit, () => new CodeBuffer()); | |
65 } | |
66 | |
67 CodeBuffer get mainBuffer { | |
68 return getBuffer(compiler.deferredLoadTask.mainOutputUnit); | |
69 } | |
70 | |
71 /** | |
72 * List of expressions and statements that will be included in the | |
73 * precompiled function. | |
74 * | |
75 * To save space, dart2js normally generates constructors and accessors | |
76 * dynamically. This doesn't work in CSP mode, and may impact startup time | |
77 * negatively. So dart2js will emit these functions to a separate file that | |
78 * can be optionally included to support CSP mode or for faster startup. | |
79 */ | |
80 Map<OutputUnit, List<jsAst.Node>> _cspPrecompiledFunctions = | |
81 new Map<OutputUnit, List<jsAst.Node>>(); | |
82 | |
83 Map<OutputUnit, List<jsAst.Expression>> _cspPrecompiledConstructorNames = | |
84 new Map<OutputUnit, List<jsAst.Expression>>(); | |
85 | |
86 /** | |
87 * Accumulate properties for classes and libraries, describing their | |
88 * static/top-level members. | |
89 * Later, these members are emitted when the class or library is emitted. | |
90 * | |
91 * See [getElementDescriptor]. | |
92 */ | |
93 // TODO(ahe): Generate statics with their class, and store only libraries in | |
94 // this map. | |
95 final Map<Element, ClassBuilder> elementDescriptors = | |
96 new Map<Element, ClassBuilder>(); | |
97 | |
98 final bool generateSourceMap; | |
99 | |
100 OldEmitter(Compiler compiler, Namer namer, this.generateSourceMap, this.task) | |
101 : this.compiler = compiler, | |
102 this.namer = namer, | |
103 cachedEmittedConstants = compiler.cacheStrategy.newSet(), | |
104 cachedClassBuilders = compiler.cacheStrategy.newMap(), | |
105 cachedElements = compiler.cacheStrategy.newSet() { | |
106 constantEmitter = | |
107 new ConstantEmitter(compiler, namer, makeConstantListTemplate); | |
108 containerBuilder.emitter = this; | |
109 classEmitter.emitter = this; | |
110 nsmEmitter.emitter = this; | |
111 interceptorEmitter.emitter = this; | |
112 metadataEmitter.emitter = this; | |
113 } | |
114 | |
115 List<jsAst.Node> cspPrecompiledFunctionFor(OutputUnit outputUnit) { | |
116 return _cspPrecompiledFunctions.putIfAbsent( | |
117 outputUnit, | |
118 () => new List<jsAst.Node>()); | |
119 } | |
120 | |
121 List<jsAst.Expression> cspPrecompiledConstructorNamesFor( | |
122 OutputUnit outputUnit) { | |
123 return _cspPrecompiledConstructorNames.putIfAbsent( | |
124 outputUnit, | |
125 () => new List<jsAst.Expression>()); | |
126 } | |
127 | |
128 /// Erases the precompiled information for csp mode for all output units. | |
129 /// Used by the incremental compiler. | |
130 void clearCspPrecompiledNodes() { | |
131 _cspPrecompiledFunctions.clear(); | |
132 _cspPrecompiledConstructorNames.clear(); | |
133 } | |
134 | |
135 void addComment(String comment, CodeBuffer buffer) { | |
136 buffer.write(jsAst.prettyPrint(js.comment(comment), compiler)); | |
137 } | |
138 | |
139 jsAst.Expression constantReference(ConstantValue value) { | |
140 return constantEmitter.reference(value); | |
141 } | |
142 | |
143 jsAst.Expression constantInitializerExpression(ConstantValue value) { | |
144 return constantEmitter.initializationExpression(value); | |
145 } | |
146 | |
147 String get name => 'CodeEmitter'; | |
148 | |
149 String get currentGenerateAccessorName | |
150 => '${namer.currentIsolate}.\$generateAccessor'; | |
151 String get generateAccessorHolder | |
152 => '$isolatePropertiesName.\$generateAccessor'; | |
153 String get finishClassesProperty | |
154 => r'$finishClasses'; | |
155 String get finishClassesName | |
156 => '${namer.isolateName}.$finishClassesProperty'; | |
157 String get finishIsolateConstructorName | |
158 => '${namer.isolateName}.\$finishIsolateConstructor'; | |
159 String get isolatePropertiesName | |
160 => '${namer.isolateName}.${namer.isolatePropertiesName}'; | |
161 String get lazyInitializerName | |
162 => '${namer.isolateName}.\$lazy'; | |
163 String get initName => 'init'; | |
164 String get makeConstListProperty | |
165 => namer.getMappedInstanceName('makeConstantList'); | |
166 | |
167 /// For deferred loading we communicate the initializers via this global var. | |
168 final String deferredInitializers = r"$dart_deferred_initializers"; | |
169 | |
170 /// All the global state can be passed around with this variable. | |
171 String get globalsHolder => namer.getMappedGlobalName("globalsHolder"); | |
172 | |
173 jsAst.Expression generateEmbeddedGlobalAccess(String global) { | |
174 return js(generateEmbeddedGlobalAccessString(global)); | |
175 } | |
176 | |
177 String generateEmbeddedGlobalAccessString(String global) { | |
178 // TODO(floitsch): don't use 'init' as global embedder storage. | |
179 return '$initName.$global'; | |
180 } | |
181 | |
182 jsAst.FunctionDeclaration get generateAccessorFunction { | |
183 const RANGE1_SIZE = RANGE1_LAST - RANGE1_FIRST + 1; | |
184 const RANGE2_SIZE = RANGE2_LAST - RANGE2_FIRST + 1; | |
185 const RANGE1_ADJUST = - (FIRST_FIELD_CODE - RANGE1_FIRST); | |
186 const RANGE2_ADJUST = - (FIRST_FIELD_CODE + RANGE1_SIZE - RANGE2_FIRST); | |
187 const RANGE3_ADJUST = | |
188 - (FIRST_FIELD_CODE + RANGE1_SIZE + RANGE2_SIZE - RANGE3_FIRST); | |
189 | |
190 String receiverParamName = compiler.enableMinification ? "r" : "receiver"; | |
191 String valueParamName = compiler.enableMinification ? "v" : "value"; | |
192 String reflectableField = namer.reflectableField; | |
193 | |
194 return js.statement(''' | |
195 function generateAccessor(fieldDescriptor, accessors, cls) { | |
196 var fieldInformation = fieldDescriptor.split("-"); | |
197 var field = fieldInformation[0]; | |
198 var len = field.length; | |
199 var code = field.charCodeAt(len - 1); | |
200 var reflectable; | |
201 if (fieldInformation.length > 1) reflectable = true; | |
202 else reflectable = false; | |
203 code = ((code >= $RANGE1_FIRST) && (code <= $RANGE1_LAST)) | |
204 ? code - $RANGE1_ADJUST | |
205 : ((code >= $RANGE2_FIRST) && (code <= $RANGE2_LAST)) | |
206 ? code - $RANGE2_ADJUST | |
207 : ((code >= $RANGE3_FIRST) && (code <= $RANGE3_LAST)) | |
208 ? code - $RANGE3_ADJUST | |
209 : $NO_FIELD_CODE; | |
210 | |
211 if (code) { // needsAccessor | |
212 var getterCode = code & 3; | |
213 var setterCode = code >> 2; | |
214 var accessorName = field = field.substring(0, len - 1); | |
215 | |
216 var divider = field.indexOf(":"); | |
217 if (divider > 0) { // Colon never in first position. | |
218 accessorName = field.substring(0, divider); | |
219 field = field.substring(divider + 1); | |
220 } | |
221 | |
222 if (getterCode) { // needsGetter | |
223 var args = (getterCode & 2) ? "$receiverParamName" : ""; | |
224 var receiver = (getterCode & 1) ? "this" : "$receiverParamName"; | |
225 var body = "return " + receiver + "." + field; | |
226 var property = | |
227 cls + ".prototype.${namer.getterPrefix}" + accessorName + "="; | |
228 var fn = "function(" + args + "){" + body + "}"; | |
229 if (reflectable) | |
230 accessors.push(property + "\$reflectable(" + fn + ");\\n"); | |
231 else | |
232 accessors.push(property + fn + ";\\n"); | |
233 } | |
234 | |
235 if (setterCode) { // needsSetter | |
236 var args = (setterCode & 2) | |
237 ? "$receiverParamName,${_}$valueParamName" | |
238 : "$valueParamName"; | |
239 var receiver = (setterCode & 1) ? "this" : "$receiverParamName"; | |
240 var body = receiver + "." + field + "$_=$_$valueParamName"; | |
241 var property = | |
242 cls + ".prototype.${namer.setterPrefix}" + accessorName + "="; | |
243 var fn = "function(" + args + "){" + body + "}"; | |
244 if (reflectable) | |
245 accessors.push(property + "\$reflectable(" + fn + ");\\n"); | |
246 else | |
247 accessors.push(property + fn + ";\\n"); | |
248 } | |
249 } | |
250 | |
251 return field; | |
252 }'''); | |
253 } | |
254 | |
255 List get defineClassFunction { | |
256 // First the class name, then the field names in an array and the members | |
257 // (inside an Object literal). | |
258 // The caller can also pass in the constructor as a function if needed. | |
259 // | |
260 // Example: | |
261 // defineClass("A", ["x", "y"], { | |
262 // foo$1: function(y) { | |
263 // print(this.x + y); | |
264 // }, | |
265 // bar$2: function(t, v) { | |
266 // this.x = t - v; | |
267 // }, | |
268 // }); | |
269 | |
270 var defineClass = js('''function(name, cls, fields) { | |
271 var accessors = []; | |
272 | |
273 var str = "function " + cls + "("; | |
274 var body = ""; | |
275 | |
276 for (var i = 0; i < fields.length; i++) { | |
277 if(i != 0) str += ", "; | |
278 | |
279 var field = generateAccessor(fields[i], accessors, cls); | |
280 var parameter = "parameter_" + field; | |
281 str += parameter; | |
282 body += ("this." + field + " = " + parameter + ";\\n"); | |
283 } | |
284 str += ") {\\n" + body + "}\\n"; | |
285 str += cls + ".builtin\$cls=\\"" + name + "\\";\\n"; | |
286 str += "\$desc=\$collectedClasses." + cls + ";\\n"; | |
287 str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n"; | |
288 str += cls + ".prototype = \$desc;\\n"; | |
289 if (typeof defineClass.name != "string") { | |
290 str += cls + ".name=\\"" + cls + "\\";\\n"; | |
291 } | |
292 str += accessors.join(""); | |
293 | |
294 return str; | |
295 }'''); | |
296 // Declare a function called "generateAccessor". This is used in | |
297 // defineClassFunction (it's a local declaration in init()). | |
298 return [ | |
299 generateAccessorFunction, | |
300 js('$generateAccessorHolder = generateAccessor'), | |
301 new jsAst.FunctionDeclaration( | |
302 new jsAst.VariableDeclaration('defineClass'), defineClass) ]; | |
303 } | |
304 | |
305 /** Needs defineClass to be defined. */ | |
306 List buildInheritFrom() { | |
307 return [js(r''' | |
308 var inheritFrom = function() { | |
309 function tmp() {} | |
310 var hasOwnProperty = Object.prototype.hasOwnProperty; | |
311 return function (constructor, superConstructor) { | |
312 tmp.prototype = superConstructor.prototype; | |
313 var object = new tmp(); | |
314 var properties = constructor.prototype; | |
315 for (var member in properties) | |
316 if (hasOwnProperty.call(properties, member)) | |
317 object[member] = properties[member]; | |
318 object.constructor = constructor; | |
319 constructor.prototype = object; | |
320 return object; | |
321 }; | |
322 }() | |
323 ''')]; | |
324 } | |
325 | |
326 jsAst.Fun get finishClassesFunction { | |
327 // Class descriptions are collected in a JS object. | |
328 // 'finishClasses' takes all collected descriptions and sets up | |
329 // the prototype. | |
330 // Once set up, the constructors prototype field satisfy: | |
331 // - it contains all (local) members. | |
332 // - its internal prototype (__proto__) points to the superclass' | |
333 // prototype field. | |
334 // - the prototype's constructor field points to the JavaScript | |
335 // constructor. | |
336 // For engines where we have access to the '__proto__' we can manipulate | |
337 // the object literal directly. For other engines we have to create a new | |
338 // object and copy over the members. | |
339 | |
340 String reflectableField = namer.reflectableField; | |
341 jsAst.Expression allClassesAccess = | |
342 generateEmbeddedGlobalAccess(embeddedNames.ALL_CLASSES); | |
343 jsAst.Expression metadataAccess = | |
344 generateEmbeddedGlobalAccess(embeddedNames.METADATA); | |
345 jsAst.Expression interceptorsByTagAccess = | |
346 generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG); | |
347 jsAst.Expression leafTagsAccess = | |
348 generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS); | |
349 | |
350 return js(''' | |
351 function(collectedClasses, isolateProperties, existingIsolateProperties) { | |
352 var pendingClasses = Object.create(null); | |
353 if (!#) # = Object.create(null); // embedded allClasses. | |
354 var allClasses = #; // embedded allClasses; | |
355 | |
356 if (#) // DEBUG_FAST_OBJECTS | |
357 print("Number of classes: " + | |
358 Object.getOwnPropertyNames(\$\$).length); | |
359 | |
360 var hasOwnProperty = Object.prototype.hasOwnProperty; | |
361 | |
362 if (typeof dart_precompiled == "function") { | |
363 var constructors = dart_precompiled(collectedClasses); | |
364 } else { | |
365 var combinedConstructorFunction = | |
366 "function \$reflectable(fn){fn.$reflectableField=1;return fn};\\n"+ | |
367 "var \$desc;\\n"; | |
368 var constructorsList = []; | |
369 } | |
370 | |
371 for (var cls in collectedClasses) { | |
372 var desc = collectedClasses[cls]; | |
373 if (desc instanceof Array) desc = desc[1]; | |
374 | |
375 /* The 'fields' are either a constructor function or a | |
376 * string encoding fields, constructor and superclass. Get | |
377 * the superclass and the fields in the format | |
378 * '[name/]Super;field1,field2' | |
379 * from the CLASS_DESCRIPTOR_PROPERTY property on the descriptor. | |
380 * The 'name/' is optional and contains the name that should be used | |
381 * when printing the runtime type string. It is used, for example, | |
382 * to print the runtime type JSInt as 'int'. | |
383 */ | |
384 var classData = desc["${namer.classDescriptorProperty}"], | |
385 supr, name = cls, fields = classData; | |
386 if (#) // backend.hasRetainedMetadata | |
387 if (typeof classData == "object" && | |
388 classData instanceof Array) { | |
389 classData = fields = classData[0]; | |
390 } | |
391 if (typeof classData == "string") { | |
392 var split = classData.split("/"); | |
393 if (split.length == 2) { | |
394 name = split[0]; | |
395 fields = split[1]; | |
396 } | |
397 } | |
398 | |
399 var s = fields.split(";"); | |
400 fields = s[1] == "" ? [] : s[1].split(","); | |
401 supr = s[0]; | |
402 split = supr.split(":"); | |
403 if (split.length == 2) { | |
404 supr = split[0]; | |
405 var functionSignature = split[1]; | |
406 if (functionSignature) | |
407 desc.\$signature = (function(s) { | |
408 return function(){ return #[s]; }; // embedded metadata. | |
409 })(functionSignature); | |
410 } | |
411 | |
412 if (#) // needsMixinSupport | |
413 if (supr && supr.indexOf("+") > 0) { | |
414 s = supr.split("+"); | |
415 supr = s[0]; | |
416 var mixin = collectedClasses[s[1]]; | |
417 if (mixin instanceof Array) mixin = mixin[1]; | |
418 for (var d in mixin) { | |
419 if (hasOwnProperty.call(mixin, d) && | |
420 !hasOwnProperty.call(desc, d)) | |
421 desc[d] = mixin[d]; | |
422 } | |
423 } | |
424 | |
425 if (typeof dart_precompiled != "function") { | |
426 combinedConstructorFunction += defineClass(name, cls, fields); | |
427 constructorsList.push(cls); | |
428 } | |
429 if (supr) pendingClasses[cls] = supr; | |
430 } | |
431 | |
432 if (typeof dart_precompiled != "function") { | |
433 combinedConstructorFunction += | |
434 "return [\\n " + constructorsList.join(",\\n ") + "\\n]"; | |
435 var constructors = | |
436 new Function("\$collectedClasses", combinedConstructorFunction) | |
437 (collectedClasses); | |
438 combinedConstructorFunction = null; | |
439 } | |
440 | |
441 for (var i = 0; i < constructors.length; i++) { | |
442 var constructor = constructors[i]; | |
443 var cls = constructor.name; | |
444 var desc = collectedClasses[cls]; | |
445 var globalObject = isolateProperties; | |
446 if (desc instanceof Array) { | |
447 globalObject = desc[0] || isolateProperties; | |
448 desc = desc[1]; | |
449 } | |
450 if (#) //backend.isTreeShakingDisabled, | |
451 constructor["${namer.metadataField}"] = desc; | |
452 allClasses[cls] = constructor; | |
453 globalObject[cls] = constructor; | |
454 } | |
455 | |
456 constructors = null; | |
457 | |
458 var finishedClasses = Object.create(null); | |
459 # = Object.create(null); // embedded interceptorsByTag. | |
460 # = Object.create(null); // embedded leafTags. | |
461 | |
462 #; // buildFinishClass(), | |
463 | |
464 #; // buildTrivialNsmHandlers() | |
465 | |
466 for (var cls in pendingClasses) finishClass(cls); | |
467 }''', [ | |
468 allClassesAccess, allClassesAccess, | |
469 allClassesAccess, | |
470 DEBUG_FAST_OBJECTS, | |
471 backend.hasRetainedMetadata, | |
472 metadataAccess, | |
473 needsMixinSupport, | |
474 backend.isTreeShakingDisabled, | |
475 interceptorsByTagAccess, | |
476 leafTagsAccess, | |
477 buildFinishClass(), | |
478 nsmEmitter.buildTrivialNsmHandlers()]); | |
479 } | |
480 | |
481 jsAst.Node optional(bool condition, jsAst.Node node) { | |
482 return condition ? node : new jsAst.EmptyStatement(); | |
483 } | |
484 | |
485 jsAst.FunctionDeclaration buildFinishClass() { | |
486 String specProperty = '"${namer.nativeSpecProperty}"'; // "%" | |
487 | |
488 jsAst.Expression interceptorsByTagAccess = | |
489 generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG); | |
490 jsAst.Expression leafTagsAccess = | |
491 generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS); | |
492 | |
493 return js.statement(''' | |
494 function finishClass(cls) { | |
495 | |
496 if (finishedClasses[cls]) return; | |
497 finishedClasses[cls] = true; | |
498 | |
499 var superclass = pendingClasses[cls]; | |
500 | |
501 // The superclass is only false (empty string) for the Dart Object | |
502 // class. The minifier together with noSuchMethod can put methods on | |
503 // the Object.prototype object, and they show through here, so we check | |
504 // that we have a string. | |
505 if (!superclass || typeof superclass != "string") return; | |
506 finishClass(superclass); | |
507 var constructor = allClasses[cls]; | |
508 var superConstructor = allClasses[superclass]; | |
509 | |
510 if (!superConstructor) | |
511 superConstructor = existingIsolateProperties[superclass]; | |
512 | |
513 var prototype = inheritFrom(constructor, superConstructor); | |
514 | |
515 if (#) { // !nativeClasses.isEmpty, | |
516 // The property looks like this: | |
517 // | |
518 // HtmlElement: { | |
519 // "%": "HTMLDivElement|HTMLAnchorElement;HTMLElement;FancyButton" | |
520 // | |
521 // The first two semicolon-separated parts contain dispatch tags, the | |
522 // third contains the JavaScript names for classes. | |
523 // | |
524 // The tags indicate that JavaScript objects with the dispatch tags | |
525 // (usually constructor names) HTMLDivElement, HTMLAnchorElement and | |
526 // HTMLElement all map to the Dart native class named HtmlElement. | |
527 // The first set is for effective leaf nodes in the hierarchy, the | |
528 // second set is non-leaf nodes. | |
529 // | |
530 // The third part contains the JavaScript names of Dart classes that | |
531 // extend the native class. Here, FancyButton extends HtmlElement, so | |
532 // the runtime needs to know that window.HTMLElement.prototype is the | |
533 // prototype that needs to be extended in creating the custom element. | |
534 // | |
535 // The information is used to build tables referenced by | |
536 // getNativeInterceptor and custom element support. | |
537 if (Object.prototype.hasOwnProperty.call(prototype, $specProperty)) { | |
538 var nativeSpec = prototype[$specProperty].split(";"); | |
539 if (nativeSpec[0]) { | |
540 var tags = nativeSpec[0].split("|"); | |
541 for (var i = 0; i < tags.length; i++) { | |
542 #[tags[i]] = constructor; // embedded interceptorsByTag. | |
543 #[tags[i]] = true; // embedded leafTags. | |
544 } | |
545 } | |
546 if (nativeSpec[1]) { | |
547 tags = nativeSpec[1].split("|"); | |
548 if (#) { // User subclassing of native classes? | |
549 if (nativeSpec[2]) { | |
550 var subclasses = nativeSpec[2].split("|"); | |
551 for (var i = 0; i < subclasses.length; i++) { | |
552 var subclass = allClasses[subclasses[i]]; | |
553 subclass.\$nativeSuperclassTag = tags[0]; | |
554 } | |
555 } | |
556 for (i = 0; i < tags.length; i++) { | |
557 #[tags[i]] = constructor; // embedded interceptorsByTag. | |
558 #[tags[i]] = false; // embedded leafTags. | |
559 } | |
560 } | |
561 } | |
562 } | |
563 } | |
564 }''', [!nativeClasses.isEmpty, | |
565 interceptorsByTagAccess, | |
566 leafTagsAccess, | |
567 true, | |
568 interceptorsByTagAccess, | |
569 leafTagsAccess]); | |
570 } | |
571 | |
572 jsAst.Fun get finishIsolateConstructorFunction { | |
573 // We replace the old Isolate function with a new one that initializes | |
574 // all its fields with the initial (and often final) value of all globals. | |
575 // | |
576 // We also copy over old values like the prototype, and the | |
577 // isolateProperties themselves. | |
578 return js(''' | |
579 function (oldIsolate) { | |
580 var isolateProperties = oldIsolate.#; // isolatePropertiesName | |
581 function Isolate() { | |
582 var hasOwnProperty = Object.prototype.hasOwnProperty; | |
583 for (var staticName in isolateProperties) | |
584 if (hasOwnProperty.call(isolateProperties, staticName)) | |
585 this[staticName] = isolateProperties[staticName]; | |
586 | |
587 // Reset lazy initializers to null. | |
588 // When forcing the object to fast mode (below) v8 will consider | |
589 // functions as part the object's map. Since we will change them | |
590 // (after the first call to the getter), we would have a map | |
591 // transition. | |
592 var lazies = init.lazies; | |
593 for (var lazyInit in lazies) { | |
594 this[lazies[lazyInit]] = null; | |
595 } | |
596 | |
597 // Use the newly created object as prototype. In Chrome, | |
598 // this creates a hidden class for the object and makes | |
599 // sure it is fast to access. | |
600 function ForceEfficientMap() {} | |
601 ForceEfficientMap.prototype = this; | |
602 new ForceEfficientMap(); | |
603 | |
604 // Now, after being a fast map we can set the lazies again. | |
605 for (var lazyInit in lazies) { | |
606 var lazyInitName = lazies[lazyInit]; | |
607 this[lazyInitName] = isolateProperties[lazyInitName]; | |
608 } | |
609 } | |
610 Isolate.prototype = oldIsolate.prototype; | |
611 Isolate.prototype.constructor = Isolate; | |
612 Isolate.# = isolateProperties; // isolatePropertiesName | |
613 if (#) // needsDefineClass. | |
614 Isolate.# = oldIsolate.#; // finishClassesProperty * 2 | |
615 if (#) // outputContainsConstantList | |
616 Isolate.# = oldIsolate.#; // makeConstListProperty * 2 | |
617 return Isolate; | |
618 }''', | |
619 [namer.isolatePropertiesName, namer.isolatePropertiesName, | |
620 needsDefineClass, finishClassesProperty, finishClassesProperty, | |
621 task.outputContainsConstantList, | |
622 makeConstListProperty, makeConstListProperty ]); | |
623 } | |
624 | |
625 jsAst.Fun get lazyInitializerFunction { | |
626 String isolate = namer.currentIsolate; | |
627 jsAst.Expression cyclicThrow = | |
628 namer.elementAccess(backend.getCyclicThrowHelper()); | |
629 jsAst.Expression laziesAccess = | |
630 generateEmbeddedGlobalAccess(embeddedNames.LAZIES); | |
631 | |
632 return js(''' | |
633 function (prototype, staticName, fieldName, getterName, lazyValue) { | |
634 if (!#) # = Object.create(null); | |
635 #[fieldName] = getterName; | |
636 | |
637 var sentinelUndefined = {}; | |
638 var sentinelInProgress = {}; | |
639 prototype[fieldName] = sentinelUndefined; | |
640 | |
641 prototype[getterName] = function () { | |
642 var result = $isolate[fieldName]; | |
643 try { | |
644 if (result === sentinelUndefined) { | |
645 $isolate[fieldName] = sentinelInProgress; | |
646 | |
647 try { | |
648 result = $isolate[fieldName] = lazyValue(); | |
649 } finally { | |
650 // Use try-finally, not try-catch/throw as it destroys the | |
651 // stack trace. | |
652 if (result === sentinelUndefined) | |
653 $isolate[fieldName] = null; | |
654 } | |
655 } else { | |
656 if (result === sentinelInProgress) | |
657 #(staticName); | |
658 } | |
659 | |
660 return result; | |
661 } finally { | |
662 $isolate[getterName] = function() { return this[fieldName]; }; | |
663 } | |
664 } | |
665 } | |
666 ''', [laziesAccess, laziesAccess, | |
667 laziesAccess, | |
668 cyclicThrow]); | |
669 } | |
670 | |
671 List buildDefineClassAndFinishClassFunctionsIfNecessary() { | |
672 if (!needsDefineClass) return []; | |
673 return defineClassFunction | |
674 ..addAll(buildInheritFrom()) | |
675 ..addAll([ | |
676 js('$finishClassesName = #', finishClassesFunction) | |
677 ]); | |
678 } | |
679 | |
680 List buildLazyInitializerFunctionIfNecessary() { | |
681 if (!needsLazyInitializer) return []; | |
682 | |
683 return [js('# = #', [js(lazyInitializerName), lazyInitializerFunction])]; | |
684 } | |
685 | |
686 List buildFinishIsolateConstructor() { | |
687 return [ | |
688 js('$finishIsolateConstructorName = #', finishIsolateConstructorFunction) | |
689 ]; | |
690 } | |
691 | |
692 void emitFinishIsolateConstructorInvocation(CodeBuffer buffer) { | |
693 String isolate = namer.isolateName; | |
694 buffer.write("$isolate = $finishIsolateConstructorName($isolate)$N"); | |
695 } | |
696 | |
697 /// In minified mode we want to keep the name for the most common core types. | |
698 bool _isNativeTypeNeedingReflectionName(Element element) { | |
699 if (!element.isClass) return false; | |
700 return (element == compiler.intClass || | |
701 element == compiler.doubleClass || | |
702 element == compiler.numClass || | |
703 element == compiler.stringClass || | |
704 element == compiler.boolClass || | |
705 element == compiler.nullClass || | |
706 element == compiler.listClass); | |
707 } | |
708 | |
709 /// Returns the "reflection name" of an [Element] or [Selector]. | |
710 /// The reflection name of a getter 'foo' is 'foo'. | |
711 /// The reflection name of a setter 'foo' is 'foo='. | |
712 /// The reflection name of a method 'foo' is 'foo:N:M:O', where N is the | |
713 /// number of required arguments, M is the number of optional arguments, and | |
714 /// O is the named arguments. | |
715 /// The reflection name of a constructor is similar to a regular method but | |
716 /// starts with 'new '. | |
717 /// The reflection name of class 'C' is 'C'. | |
718 /// An anonymous mixin application has no reflection name. | |
719 /// This is used by js_mirrors.dart. | |
720 String getReflectionName(elementOrSelector, String mangledName) { | |
721 String name = elementOrSelector.name; | |
722 if (backend.shouldRetainName(name) || | |
723 elementOrSelector is Element && | |
724 // Make sure to retain names of unnamed constructors, and | |
725 // for common native types. | |
726 ((name == '' && | |
727 backend.isAccessibleByReflection(elementOrSelector)) || | |
728 _isNativeTypeNeedingReflectionName(elementOrSelector))) { | |
729 | |
730 // TODO(ahe): Enable the next line when I can tell the difference between | |
731 // an instance method and a global. They may have the same mangled name. | |
732 // if (recordedMangledNames.contains(mangledName)) return null; | |
733 recordedMangledNames.add(mangledName); | |
734 return getReflectionNameInternal(elementOrSelector, mangledName); | |
735 } | |
736 return null; | |
737 } | |
738 | |
739 String getReflectionNameInternal(elementOrSelector, String mangledName) { | |
740 String name = | |
741 namer.privateName(elementOrSelector.library, elementOrSelector.name); | |
742 if (elementOrSelector.isGetter) return name; | |
743 if (elementOrSelector.isSetter) { | |
744 if (!mangledName.startsWith(namer.setterPrefix)) return '$name='; | |
745 String base = mangledName.substring(namer.setterPrefix.length); | |
746 String getter = '${namer.getterPrefix}$base'; | |
747 mangledFieldNames.putIfAbsent(getter, () => name); | |
748 assert(mangledFieldNames[getter] == name); | |
749 recordedMangledNames.add(getter); | |
750 // TODO(karlklose,ahe): we do not actually need to store information | |
751 // about the name of this setter in the output, but it is needed for | |
752 // marking the function as invokable by reflection. | |
753 return '$name='; | |
754 } | |
755 if (elementOrSelector is Element && elementOrSelector.isClosure) { | |
756 // Closures are synthesized and their name might conflict with existing | |
757 // globals. Assign an illegal name, and make sure they don't clash | |
758 // with each other. | |
759 return " $mangledName"; | |
760 } | |
761 if (elementOrSelector is Selector | |
762 || elementOrSelector.isFunction | |
763 || elementOrSelector.isConstructor) { | |
764 int requiredParameterCount; | |
765 int optionalParameterCount; | |
766 String namedArguments = ''; | |
767 bool isConstructor = false; | |
768 if (elementOrSelector is Selector) { | |
769 Selector selector = elementOrSelector; | |
770 requiredParameterCount = selector.argumentCount; | |
771 optionalParameterCount = 0; | |
772 namedArguments = namedParametersAsReflectionNames(selector); | |
773 } else { | |
774 FunctionElement function = elementOrSelector; | |
775 if (function.isConstructor) { | |
776 isConstructor = true; | |
777 name = Elements.reconstructConstructorName(function); | |
778 } | |
779 FunctionSignature signature = function.functionSignature; | |
780 requiredParameterCount = signature.requiredParameterCount; | |
781 optionalParameterCount = signature.optionalParameterCount; | |
782 if (signature.optionalParametersAreNamed) { | |
783 var names = []; | |
784 for (Element e in signature.optionalParameters) { | |
785 names.add(e.name); | |
786 } | |
787 Selector selector = new Selector.call( | |
788 function.name, | |
789 function.library, | |
790 requiredParameterCount, | |
791 names); | |
792 namedArguments = namedParametersAsReflectionNames(selector); | |
793 } else { | |
794 // Named parameters are handled differently by mirrors. For unnamed | |
795 // parameters, they are actually required if invoked | |
796 // reflectively. Also, if you have a method c(x) and c([x]) they both | |
797 // get the same mangled name, so they must have the same reflection | |
798 // name. | |
799 requiredParameterCount += optionalParameterCount; | |
800 optionalParameterCount = 0; | |
801 } | |
802 } | |
803 String suffix = | |
804 // TODO(ahe): We probably don't need optionalParameterCount in the | |
805 // reflection name. | |
806 '$name:$requiredParameterCount:$optionalParameterCount' | |
807 '$namedArguments'; | |
808 return (isConstructor) ? 'new $suffix' : suffix; | |
809 } | |
810 Element element = elementOrSelector; | |
811 if (element.isGenerativeConstructorBody) { | |
812 return null; | |
813 } else if (element.isClass) { | |
814 ClassElement cls = element; | |
815 if (cls.isUnnamedMixinApplication) return null; | |
816 return cls.name; | |
817 } else if (element.isTypedef) { | |
818 return element.name; | |
819 } | |
820 throw compiler.internalError(element, | |
821 'Do not know how to reflect on this $element.'); | |
822 } | |
823 | |
824 String namedParametersAsReflectionNames(Selector selector) { | |
825 if (selector.getOrderedNamedArguments().isEmpty) return ''; | |
826 String names = selector.getOrderedNamedArguments().join(':'); | |
827 return ':$names'; | |
828 } | |
829 | |
830 jsAst.FunctionDeclaration buildCspPrecompiledFunctionFor( | |
831 OutputUnit outputUnit) { | |
832 // TODO(ahe): Compute a hash code. | |
833 return js.statement(''' | |
834 function dart_precompiled(\$collectedClasses) { | |
835 var \$desc; | |
836 #; | |
837 return #; | |
838 }''', | |
839 [cspPrecompiledFunctionFor(outputUnit), | |
840 new jsAst.ArrayInitializer.from( | |
841 cspPrecompiledConstructorNamesFor(outputUnit))]); | |
842 } | |
843 | |
844 void generateClass(ClassElement classElement, ClassBuilder properties) { | |
845 compiler.withCurrentElement(classElement, () { | |
846 if (compiler.hasIncrementalSupport) { | |
847 ClassBuilder builder = | |
848 cachedClassBuilders.putIfAbsent(classElement, () { | |
849 ClassBuilder builder = new ClassBuilder(classElement, namer); | |
850 classEmitter.generateClass( | |
851 classElement, builder, additionalProperties[classElement]); | |
852 return builder; | |
853 }); | |
854 invariant(classElement, builder.fields.isEmpty); | |
855 invariant(classElement, builder.superName == null); | |
856 invariant(classElement, builder.functionType == null); | |
857 invariant(classElement, builder.fieldMetadata == null); | |
858 properties.properties.addAll(builder.properties); | |
859 } else { | |
860 classEmitter.generateClass( | |
861 classElement, properties, additionalProperties[classElement]); | |
862 } | |
863 }); | |
864 } | |
865 | |
866 void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) { | |
867 if (needsDefineClass) { | |
868 buffer.write('$finishClassesName($classesCollector,' | |
869 '$_$isolateProperties,' | |
870 '${_}null)$N'); | |
871 | |
872 // Reset the map. | |
873 buffer.write("$classesCollector$_=${_}null$N$n"); | |
874 } | |
875 } | |
876 | |
877 void emitStaticFunctions(List<Element> staticFunctions) { | |
878 for (Element element in staticFunctions) { | |
879 ClassBuilder builder = new ClassBuilder(element, namer); | |
880 containerBuilder.addMember(element, builder); | |
881 getElementDescriptor(element).properties.addAll(builder.properties); | |
882 } | |
883 } | |
884 | |
885 void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) { | |
886 JavaScriptConstantCompiler handler = backend.constants; | |
887 Iterable<VariableElement> staticNonFinalFields = | |
888 handler.getStaticNonFinalFieldsForEmission(); | |
889 for (Element element in Elements.sortedByPosition(staticNonFinalFields)) { | |
890 // [:interceptedNames:] is handled in [emitInterceptedNames]. | |
891 if (element == backend.interceptedNames) continue; | |
892 // `mapTypeToInterceptor` is handled in [emitMapTypeToInterceptor]. | |
893 if (element == backend.mapTypeToInterceptor) continue; | |
894 compiler.withCurrentElement(element, () { | |
895 ConstantValue initialValue = handler.getInitialValueFor(element).value; | |
896 jsAst.Expression init = | |
897 js('$isolateProperties.# = #', | |
898 [namer.getNameOfGlobalField(element), | |
899 constantEmitter.referenceInInitializationContext(initialValue)]); | |
900 buffer.write(jsAst.prettyPrint(init, compiler, | |
901 monitor: compiler.dumpInfoTask)); | |
902 buffer.write('$N'); | |
903 }); | |
904 } | |
905 } | |
906 | |
907 void emitLazilyInitializedStaticFields(CodeBuffer buffer) { | |
908 JavaScriptConstantCompiler handler = backend.constants; | |
909 List<VariableElement> lazyFields = | |
910 handler.getLazilyInitializedFieldsForEmission(); | |
911 if (!lazyFields.isEmpty) { | |
912 needsLazyInitializer = true; | |
913 for (VariableElement element in Elements.sortedByPosition(lazyFields)) { | |
914 jsAst.Expression code = backend.generatedCode[element]; | |
915 // The code is null if we ended up not needing the lazily | |
916 // initialized field after all because of constant folding | |
917 // before code generation. | |
918 if (code == null) continue; | |
919 // The code only computes the initial value. We build the lazy-check | |
920 // here: | |
921 // lazyInitializer(prototype, 'name', fieldName, getterName, initial); | |
922 // The name is used for error reporting. The 'initial' must be a | |
923 // closure that constructs the initial value. | |
924 jsAst.Expression init = js('#(#,#,#,#,#)', | |
925 [js(lazyInitializerName), | |
926 js(isolateProperties), | |
927 js.string(element.name), | |
928 js.string(namer.getNameX(element)), | |
929 js.string(namer.getLazyInitializerName(element)), | |
930 code]); | |
931 buffer.write(jsAst.prettyPrint(init, compiler, | |
932 monitor: compiler.dumpInfoTask)); | |
933 buffer.write("$N"); | |
934 } | |
935 } | |
936 } | |
937 | |
938 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) { | |
939 if (constant.isFunction) return true; // Already emitted. | |
940 if (constant.isPrimitive) return true; // Inlined. | |
941 if (constant.isDummy) return true; // Inlined. | |
942 // The name is null when the constant is already a JS constant. | |
943 // TODO(floitsch): every constant should be registered, so that we can | |
944 // share the ones that take up too much space (like some strings). | |
945 if (namer.constantName(constant) == null) return true; | |
946 return false; | |
947 } | |
948 | |
949 int compareConstants(ConstantValue a, ConstantValue b) { | |
950 // Inlined constants don't affect the order and sometimes don't even have | |
951 // names. | |
952 int cmp1 = isConstantInlinedOrAlreadyEmitted(a) ? 0 : 1; | |
953 int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1; | |
954 if (cmp1 + cmp2 < 2) return cmp1 - cmp2; | |
955 | |
956 // Emit constant interceptors first. Constant interceptors for primitives | |
957 // might be used by code that builds other constants. See Issue 18173. | |
958 if (a.isInterceptor != b.isInterceptor) { | |
959 return a.isInterceptor ? -1 : 1; | |
960 } | |
961 | |
962 // Sorting by the long name clusters constants with the same constructor | |
963 // which compresses a tiny bit better. | |
964 int r = namer.constantLongName(a).compareTo(namer.constantLongName(b)); | |
965 if (r != 0) return r; | |
966 // Resolve collisions in the long name by using the constant name (i.e. JS | |
967 // name) which is unique. | |
968 return namer.constantName(a).compareTo(namer.constantName(b)); | |
969 } | |
970 | |
971 void emitCompileTimeConstants(CodeBuffer buffer, OutputUnit outputUnit) { | |
972 List<ConstantValue> constants = outputConstantLists[outputUnit]; | |
973 if (constants == null) return; | |
974 bool isMainBuffer = buffer == mainBuffer; | |
975 if (compiler.hasIncrementalSupport && isMainBuffer) { | |
976 buffer = cachedEmittedConstantsBuffer; | |
977 } | |
978 for (ConstantValue constant in constants) { | |
979 if (compiler.hasIncrementalSupport && isMainBuffer) { | |
980 if (cachedEmittedConstants.contains(constant)) continue; | |
981 cachedEmittedConstants.add(constant); | |
982 } | |
983 String name = namer.constantName(constant); | |
984 jsAst.Expression init = js('#.# = #', | |
985 [namer.globalObjectForConstant(constant), name, | |
986 constantInitializerExpression(constant)]); | |
987 buffer.write(jsAst.prettyPrint(init, compiler, | |
988 monitor: compiler.dumpInfoTask)); | |
989 buffer.write('$N'); | |
990 } | |
991 if (compiler.hasIncrementalSupport && isMainBuffer) { | |
992 mainBuffer.add(cachedEmittedConstantsBuffer); | |
993 } | |
994 } | |
995 | |
996 jsAst.Template get makeConstantListTemplate { | |
997 // TODO(floitsch): there is no harm in caching the template. | |
998 return jsAst.js.uncachedExpressionTemplate( | |
999 '${namer.isolateName}.$makeConstListProperty(#)'); | |
1000 } | |
1001 | |
1002 void emitMakeConstantList(CodeBuffer buffer) { | |
1003 buffer.write( | |
1004 jsAst.prettyPrint( | |
1005 // Functions are stored in the hidden class and not as properties in | |
1006 // the object. We never actually look at the value, but only want | |
1007 // to know if the property exists. | |
1008 js.statement(r'''#.# = function(list) { | |
1009 list.immutable$list = Array; | |
1010 list.fixed$length = Array; | |
1011 return list; | |
1012 }''', | |
1013 [namer.isolateName, makeConstListProperty]), | |
1014 compiler, monitor: compiler.dumpInfoTask)); | |
1015 buffer.write(N); | |
1016 } | |
1017 | |
1018 /// Returns the code equivalent to: | |
1019 /// `function(args) { $.startRootIsolate(X.main$closure(), args); }` | |
1020 jsAst.Expression buildIsolateSetupClosure(Element appMain, | |
1021 Element isolateMain) { | |
1022 jsAst.Expression mainAccess = namer.isolateStaticClosureAccess(appMain); | |
1023 // Since we pass the closurized version of the main method to | |
1024 // the isolate method, we must make sure that it exists. | |
1025 return js('function(a){ #(#, a); }', | |
1026 [namer.elementAccess(isolateMain), mainAccess]); | |
1027 } | |
1028 | |
1029 /** | |
1030 * Emits code that sets the `isolateTag embedded global to a unique string. | |
1031 */ | |
1032 jsAst.Expression generateIsolateAffinityTagInitialization() { | |
1033 jsAst.Expression getIsolateTagAccess = | |
1034 generateEmbeddedGlobalAccess(embeddedNames.GET_ISOLATE_TAG); | |
1035 jsAst.Expression isolateTagAccess = | |
1036 generateEmbeddedGlobalAccess(embeddedNames.ISOLATE_TAG); | |
1037 | |
1038 return js(''' | |
1039 !function() { | |
1040 // On V8, the 'intern' function converts a string to a symbol, which | |
1041 // makes property access much faster. | |
1042 function intern(s) { | |
1043 var o = {}; | |
1044 o[s] = 1; | |
1045 return Object.keys(convertToFastObject(o))[0]; | |
1046 } | |
1047 | |
1048 # = function(name) { // embedded getIsolateTag | |
1049 return intern("___dart_" + name + #); // embedded isolateTag | |
1050 }; | |
1051 | |
1052 // To ensure that different programs loaded into the same context (page) | |
1053 // use distinct dispatch properies, we place an object on `Object` to | |
1054 // contain the names already in use. | |
1055 var tableProperty = "___dart_isolate_tags_"; | |
1056 var usedProperties = Object[tableProperty] || | |
1057 (Object[tableProperty] = Object.create(null)); | |
1058 | |
1059 var rootProperty = "_${generateIsolateTagRoot()}"; | |
1060 for (var i = 0; ; i++) { | |
1061 var property = intern(rootProperty + "_" + i + "_"); | |
1062 if (!(property in usedProperties)) { | |
1063 usedProperties[property] = 1; | |
1064 # = property; // embedded isolateTag | |
1065 break; | |
1066 } | |
1067 } | |
1068 }() | |
1069 ''', [getIsolateTagAccess, | |
1070 isolateTagAccess, | |
1071 isolateTagAccess]); | |
1072 } | |
1073 | |
1074 jsAst.Expression generateDispatchPropertyNameInitialization() { | |
1075 jsAst.Expression dispatchPropertyNameAccess = | |
1076 generateEmbeddedGlobalAccess(embeddedNames.DISPATCH_PROPERTY_NAME); | |
1077 jsAst.Expression getIsolateTagAccess = | |
1078 generateEmbeddedGlobalAccess(embeddedNames.GET_ISOLATE_TAG); | |
1079 return js('# = #("dispatch_record")', | |
1080 [dispatchPropertyNameAccess, | |
1081 getIsolateTagAccess]); | |
1082 } | |
1083 | |
1084 String generateIsolateTagRoot() { | |
1085 // TODO(sra): MD5 of contributing source code or URIs? | |
1086 return 'ZxYxX'; | |
1087 } | |
1088 | |
1089 emitMain(CodeBuffer buffer) { | |
1090 if (compiler.isMockCompilation) return; | |
1091 Element main = compiler.mainFunction; | |
1092 jsAst.Expression mainCallClosure = null; | |
1093 if (compiler.hasIsolateSupport) { | |
1094 Element isolateMain = | |
1095 backend.isolateHelperLibrary.find(JavaScriptBackend.START_ROOT_ISOLATE); | |
1096 mainCallClosure = buildIsolateSetupClosure(main, isolateMain); | |
1097 } else if (compiler.hasIncrementalSupport) { | |
1098 mainCallClosure = | |
1099 js('function() { return #(); }', namer.elementAccess(main)); | |
1100 } else { | |
1101 mainCallClosure = namer.elementAccess(main); | |
1102 } | |
1103 | |
1104 if (backend.needToInitializeIsolateAffinityTag) { | |
1105 buffer.write( | |
1106 jsAst.prettyPrint(generateIsolateAffinityTagInitialization(), | |
1107 compiler, monitor: compiler.dumpInfoTask)); | |
1108 buffer.write(N); | |
1109 } | |
1110 if (backend.needToInitializeDispatchProperty) { | |
1111 assert(backend.needToInitializeIsolateAffinityTag); | |
1112 buffer.write( | |
1113 jsAst.prettyPrint(generateDispatchPropertyNameInitialization(), | |
1114 compiler, monitor: compiler.dumpInfoTask)); | |
1115 buffer.write(N); | |
1116 } | |
1117 | |
1118 jsAst.Expression currentScriptAccess = | |
1119 generateEmbeddedGlobalAccess(embeddedNames.CURRENT_SCRIPT); | |
1120 | |
1121 addComment('BEGIN invoke [main].', buffer); | |
1122 // This code finds the currently executing script by listening to the | |
1123 // onload event of all script tags and getting the first script which | |
1124 // finishes. Since onload is called immediately after execution this should | |
1125 // not substantially change execution order. | |
1126 jsAst.Statement invokeMain = js.statement(''' | |
1127 (function (callback) { | |
1128 if (typeof document === "undefined") { | |
1129 callback(null); | |
1130 return; | |
1131 } | |
1132 if (document.currentScript) { | |
1133 callback(document.currentScript); | |
1134 return; | |
1135 } | |
1136 | |
1137 var scripts = document.scripts; | |
1138 function onLoad(event) { | |
1139 for (var i = 0; i < scripts.length; ++i) { | |
1140 scripts[i].removeEventListener("load", onLoad, false); | |
1141 } | |
1142 callback(event.target); | |
1143 } | |
1144 for (var i = 0; i < scripts.length; ++i) { | |
1145 scripts[i].addEventListener("load", onLoad, false); | |
1146 } | |
1147 })(function(currentScript) { | |
1148 # = currentScript; // embedded currentScript. | |
1149 | |
1150 if (typeof dartMainRunner === "function") { | |
1151 dartMainRunner(#, []); // mainCallClosure. | |
1152 } else { | |
1153 #([]); // mainCallClosure. | |
1154 } | |
1155 })$N''', [currentScriptAccess, | |
1156 mainCallClosure, | |
1157 mainCallClosure]); | |
1158 | |
1159 buffer.write(';'); | |
1160 buffer.write(jsAst.prettyPrint(invokeMain, | |
1161 compiler, monitor: compiler.dumpInfoTask)); | |
1162 buffer.write(N); | |
1163 addComment('END invoke [main].', buffer); | |
1164 } | |
1165 | |
1166 void emitInitFunction(CodeBuffer buffer) { | |
1167 jsAst.FunctionDeclaration decl = js.statement(''' | |
1168 function init() { | |
1169 $isolateProperties = Object.create(null); | |
1170 #; #; #; | |
1171 }''', [ | |
1172 buildDefineClassAndFinishClassFunctionsIfNecessary(), | |
1173 buildLazyInitializerFunctionIfNecessary(), | |
1174 buildFinishIsolateConstructor()]); | |
1175 | |
1176 buffer.write(jsAst.prettyPrint(decl, | |
1177 compiler, monitor: compiler.dumpInfoTask).getText()); | |
1178 if (compiler.enableMinification) buffer.write('\n'); | |
1179 } | |
1180 | |
1181 void emitConvertToFastObjectFunction() { | |
1182 List<jsAst.Statement> debugCode = <jsAst.Statement>[]; | |
1183 if (DEBUG_FAST_OBJECTS) { | |
1184 debugCode.add(js.statement(r''' | |
1185 // The following only works on V8 when run with option | |
1186 // "--allow-natives-syntax". We use'new Function' because the | |
1187 // miniparser does not understand V8 native syntax. | |
1188 if (typeof print === "function") { | |
1189 var HasFastProperties = | |
1190 new Function("a", "return %HasFastProperties(a)"); | |
1191 print("Size of global object: " | |
1192 + String(Object.getOwnPropertyNames(properties).length) | |
1193 + ", fast properties " + HasFastProperties(properties)); | |
1194 }''')); | |
1195 } | |
1196 | |
1197 jsAst.Statement convertToFastObject = js.statement(r''' | |
1198 function convertToFastObject(properties) { | |
1199 // Create an instance that uses 'properties' as prototype. This should | |
1200 // make 'properties' a fast object. | |
1201 function MyClass() {}; | |
1202 MyClass.prototype = properties; | |
1203 new MyClass(); | |
1204 #; | |
1205 return properties; | |
1206 }''', [debugCode]); | |
1207 | |
1208 mainBuffer.add(jsAst.prettyPrint(convertToFastObject, compiler)); | |
1209 mainBuffer.add(N); | |
1210 } | |
1211 | |
1212 void writeLibraryDescriptors(CodeBuffer buffer, LibraryElement library) { | |
1213 var uri = ""; | |
1214 if (!compiler.enableMinification || backend.mustPreserveUris) { | |
1215 uri = library.canonicalUri; | |
1216 if (uri.scheme == 'file' && compiler.outputUri != null) { | |
1217 uri = relativize(compiler.outputUri, library.canonicalUri, false); | |
1218 } | |
1219 } | |
1220 ClassBuilder descriptor = elementDescriptors[library]; | |
1221 if (descriptor == null) { | |
1222 // Nothing of the library was emitted. | |
1223 // TODO(floitsch): this should not happen. We currently have an example | |
1224 // with language/prefix6_negative_test.dart where we have an instance | |
1225 // method without its corresponding class. | |
1226 return; | |
1227 } | |
1228 | |
1229 String libraryName = | |
1230 (!compiler.enableMinification || backend.mustRetainLibraryNames) ? | |
1231 library.getLibraryName() : | |
1232 ""; | |
1233 | |
1234 jsAst.Fun metadata = metadataEmitter.buildMetadataFunction(library); | |
1235 | |
1236 jsAst.ObjectInitializer initializers = descriptor.toObjectInitializer(); | |
1237 | |
1238 compiler.dumpInfoTask.registerElementAst(library, metadata); | |
1239 compiler.dumpInfoTask.registerElementAst(library, initializers); | |
1240 buffer | |
1241 ..write('["$libraryName",$_') | |
1242 ..write('"${uri}",$_') | |
1243 ..write(metadata == null ? "" : jsAst.prettyPrint(metadata, | |
1244 compiler, | |
1245 monitor: compiler.dumpInfoTask)) | |
1246 ..write(',$_') | |
1247 ..write(namer.globalObjectFor(library)) | |
1248 ..write(',$_') | |
1249 ..write(jsAst.prettyPrint(initializers, | |
1250 compiler, | |
1251 monitor: compiler.dumpInfoTask)) | |
1252 ..write(library == compiler.mainApp ? ',${n}1' : "") | |
1253 ..write('],$n'); | |
1254 } | |
1255 | |
1256 void emitPrecompiledConstructor(OutputUnit outputUnit, | |
1257 String constructorName, | |
1258 jsAst.Expression constructorAst) { | |
1259 cspPrecompiledFunctionFor(outputUnit).add( | |
1260 new jsAst.FunctionDeclaration( | |
1261 new jsAst.VariableDeclaration(constructorName), constructorAst)); | |
1262 cspPrecompiledFunctionFor(outputUnit).add( | |
1263 js.statement(r'''{ | |
1264 #.builtin$cls = #; | |
1265 if (!"name" in #) | |
1266 #.name = #; | |
1267 $desc=$collectedClasses.#; | |
1268 if ($desc instanceof Array) $desc = $desc[1]; | |
1269 #.prototype = $desc; | |
1270 }''', | |
1271 [ constructorName, js.string(constructorName), | |
1272 constructorName, | |
1273 constructorName, js.string(constructorName), | |
1274 constructorName, | |
1275 constructorName | |
1276 ])); | |
1277 | |
1278 cspPrecompiledConstructorNamesFor(outputUnit).add(js('#', constructorName)); | |
1279 } | |
1280 | |
1281 /// Extracts the output name of the compiler's outputUri. | |
1282 String deferredPartFileName(OutputUnit outputUnit, | |
1283 {bool addExtension: true}) { | |
1284 String outPath = compiler.outputUri != null | |
1285 ? compiler.outputUri.path | |
1286 : "out"; | |
1287 String outName = outPath.substring(outPath.lastIndexOf('/') + 1); | |
1288 String extension = addExtension ? ".part.js" : ""; | |
1289 if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) { | |
1290 return "$outName$extension"; | |
1291 } else { | |
1292 String name = outputUnit.name; | |
1293 return "${outName}_$name$extension"; | |
1294 } | |
1295 } | |
1296 | |
1297 void emitLibraries(Iterable<LibraryElement> libraries) { | |
1298 if (libraries.isEmpty) return; | |
1299 | |
1300 // TODO(karlklose): document what kinds of fields this loop adds to the | |
1301 // library class builder. | |
1302 for (LibraryElement element in libraries) { | |
1303 LibraryElement library = element; | |
1304 ClassBuilder builder = new ClassBuilder(library, namer); | |
1305 if (classEmitter.emitFields(library, builder, null, emitStatics: true)) { | |
1306 jsAst.ObjectInitializer initializer = builder.toObjectInitializer(); | |
1307 compiler.dumpInfoTask.registerElementAst(builder.element, initializer); | |
1308 getElementDescriptor(library).properties.addAll(initializer.properties); | |
1309 } | |
1310 } | |
1311 } | |
1312 | |
1313 void emitTypedefs() { | |
1314 OutputUnit mainOutputUnit = compiler.deferredLoadTask.mainOutputUnit; | |
1315 | |
1316 // Emit all required typedef declarations into the main output unit. | |
1317 // TODO(karlklose): unify required classes and typedefs to declarations | |
1318 // and have builders for each kind. | |
1319 for (TypedefElement typedef in typedefsNeededForReflection) { | |
1320 OutputUnit mainUnit = compiler.deferredLoadTask.mainOutputUnit; | |
1321 LibraryElement library = typedef.library; | |
1322 // TODO(karlklose): add a TypedefBuilder and move this code there. | |
1323 DartType type = typedef.alias; | |
1324 int typeIndex = metadataEmitter.reifyType(type); | |
1325 String typeReference = | |
1326 encoding.encodeTypedefFieldDescriptor(typeIndex); | |
1327 jsAst.Property descriptor = new jsAst.Property( | |
1328 js.string(namer.classDescriptorProperty), | |
1329 js.string(typeReference)); | |
1330 jsAst.Node declaration = new jsAst.ObjectInitializer([descriptor]); | |
1331 String mangledName = namer.getNameX(typedef); | |
1332 String reflectionName = getReflectionName(typedef, mangledName); | |
1333 getElementDescriptor(library) | |
1334 ..addProperty(mangledName, declaration) | |
1335 ..addProperty("+$reflectionName", js.string('')); | |
1336 // Also emit a trivial constructor for CSP mode. | |
1337 String constructorName = mangledName; | |
1338 jsAst.Expression constructorAst = js('function() {}'); | |
1339 emitPrecompiledConstructor(mainOutputUnit, | |
1340 constructorName, | |
1341 constructorAst); | |
1342 } | |
1343 } | |
1344 | |
1345 void emitMangledNames() { | |
1346 if (!mangledFieldNames.isEmpty) { | |
1347 var keys = mangledFieldNames.keys.toList(); | |
1348 keys.sort(); | |
1349 var properties = []; | |
1350 for (String key in keys) { | |
1351 var value = js.string('${mangledFieldNames[key]}'); | |
1352 properties.add(new jsAst.Property(js.string(key), value)); | |
1353 } | |
1354 | |
1355 jsAst.Expression mangledNamesAccess = | |
1356 generateEmbeddedGlobalAccess(embeddedNames.MANGLED_NAMES); | |
1357 var map = new jsAst.ObjectInitializer(properties); | |
1358 mainBuffer.write( | |
1359 jsAst.prettyPrint( | |
1360 js.statement('# = #', [mangledNamesAccess, map]), | |
1361 compiler, | |
1362 monitor: compiler.dumpInfoTask)); | |
1363 if (compiler.enableMinification) { | |
1364 mainBuffer.write(';'); | |
1365 } | |
1366 } | |
1367 if (!mangledGlobalFieldNames.isEmpty) { | |
1368 var keys = mangledGlobalFieldNames.keys.toList(); | |
1369 keys.sort(); | |
1370 var properties = []; | |
1371 for (String key in keys) { | |
1372 var value = js.string('${mangledGlobalFieldNames[key]}'); | |
1373 properties.add(new jsAst.Property(js.string(key), value)); | |
1374 } | |
1375 jsAst.Expression mangledGlobalNamesAccess = | |
1376 generateEmbeddedGlobalAccess(embeddedNames.MANGLED_GLOBAL_NAMES); | |
1377 var map = new jsAst.ObjectInitializer(properties); | |
1378 mainBuffer.write( | |
1379 jsAst.prettyPrint( | |
1380 js.statement('# = #', [mangledGlobalNamesAccess, map]), | |
1381 compiler, | |
1382 monitor: compiler.dumpInfoTask)); | |
1383 if (compiler.enableMinification) { | |
1384 mainBuffer.write(';'); | |
1385 } | |
1386 } | |
1387 } | |
1388 | |
1389 void checkEverythingEmitted(Iterable<Element> elements) { | |
1390 List<Element> pendingStatics; | |
1391 if (!compiler.hasIncrementalSupport) { | |
1392 pendingStatics = | |
1393 Elements.sortedByPosition(elements.where((e) => !e.isLibrary)); | |
1394 | |
1395 pendingStatics.forEach((element) => | |
1396 compiler.reportInfo( | |
1397 element, MessageKind.GENERIC, {'text': 'Pending statics.'})); | |
1398 } | |
1399 | |
1400 if (pendingStatics != null && !pendingStatics.isEmpty) { | |
1401 compiler.internalError(pendingStatics.first, | |
1402 'Pending statics (see above).'); | |
1403 } | |
1404 } | |
1405 | |
1406 void emitMainOutputUnit(Map<OutputUnit, String> deferredLoadHashes, | |
1407 CodeBuffer nativeBuffer) { | |
1408 bool isProgramSplit = compiler.deferredLoadTask.isProgramSplit; | |
1409 OutputUnit mainOutputUnit = compiler.deferredLoadTask.mainOutputUnit; | |
1410 | |
1411 mainBuffer.add(buildGeneratedBy()); | |
1412 addComment(HOOKS_API_USAGE, mainBuffer); | |
1413 | |
1414 if (isProgramSplit) { | |
1415 /// For deferred loading we communicate the initializers via this global | |
1416 /// variable. The deferred hunks will add their initialization to this. | |
1417 /// The semicolon is important in minified mode, without it the | |
1418 /// following parenthesis looks like a call to the object literal. | |
1419 mainBuffer..add( | |
1420 'self.${deferredInitializers} = self.${deferredInitializers} || ' | |
1421 'Object.create(null);$n'); | |
1422 } | |
1423 | |
1424 // Using a named function here produces easier to read stack traces in | |
1425 // Chrome/V8. | |
1426 mainBuffer.add('(function(${namer.currentIsolate})$_{\n'); | |
1427 if (compiler.hasIncrementalSupport) { | |
1428 mainBuffer.add( | |
1429 '(this.\$dart_unsafe_eval =' | |
1430 ' this.\$dart_unsafe_eval || Object.create(null))' | |
1431 '.patch = function(a) { eval(a) }$N'); | |
1432 } | |
1433 if (isProgramSplit) { | |
1434 /// We collect all the global state of the, so it can be passed to the | |
1435 /// initializer of deferred files. | |
1436 mainBuffer.add('var ${globalsHolder}$_=${_}Object.create(null)$N'); | |
1437 } | |
1438 mainBuffer.add('function dart()$_{$n' | |
1439 '${_}${_}this.x$_=${_}0$N' | |
1440 '${_}${_}delete this.x$N' | |
1441 '}$n'); | |
1442 for (String globalObject in Namer.reservedGlobalObjectNames) { | |
1443 // The global objects start as so-called "slow objects". For V8, this | |
1444 // means that it won't try to make map transitions as we add properties | |
1445 // to these objects. Later on, we attempt to turn these objects into | |
1446 // fast objects by calling "convertToFastObject" (see | |
1447 // [emitConvertToFastObjectFunction]). | |
1448 mainBuffer.write('var ${globalObject}$_=${_}'); | |
1449 if(isProgramSplit) { | |
1450 mainBuffer.add('${globalsHolder}.$globalObject$_=${_}'); | |
1451 } | |
1452 mainBuffer.write('new dart$N'); | |
1453 } | |
1454 | |
1455 mainBuffer.add('function ${namer.isolateName}()$_{}\n'); | |
1456 if (isProgramSplit) { | |
1457 mainBuffer | |
1458 .write('${globalsHolder}.${namer.isolateName}$_=$_' | |
1459 '${namer.isolateName}$N' | |
1460 '${globalsHolder}.$initName$_=${_}$initName$N'); | |
1461 } | |
1462 mainBuffer.add('init()$N$n'); | |
1463 mainBuffer.add('$isolateProperties$_=$_$isolatePropertiesName$N'); | |
1464 | |
1465 emitStaticFunctions(task.outputStaticLists[mainOutputUnit]); | |
1466 | |
1467 // Only output the classesCollector if we actually have any classes. | |
1468 if (!(nativeClasses.isEmpty && | |
1469 compiler.codegenWorld.staticFunctionsNeedingGetter.isEmpty && | |
1470 outputClassLists.values.every((classList) => classList.isEmpty) && | |
1471 typedefsNeededForReflection.isEmpty)) { | |
1472 // Shorten the code by using "$$" as temporary. | |
1473 classesCollector = r"$$"; | |
1474 mainBuffer.add('var $classesCollector$_=${_}Object.create(null)$N$n'); | |
1475 } | |
1476 | |
1477 if (!nativeClasses.isEmpty) { | |
1478 addComment('Native classes', mainBuffer); | |
1479 } | |
1480 | |
1481 List<ClassElement> classes = task.outputClassLists[mainOutputUnit]; | |
1482 if (classes != null) { | |
1483 for (ClassElement element in classes) { | |
1484 generateClass(element, getElementDescriptor(element)); | |
1485 } | |
1486 } | |
1487 | |
1488 if (compiler.enableMinification) { | |
1489 mainBuffer.write(';'); | |
1490 } | |
1491 | |
1492 if (elementDescriptors.isNotEmpty) { | |
1493 Iterable<LibraryElement> libraries = | |
1494 task.outputLibraryLists[mainOutputUnit]; | |
1495 if (libraries == null) libraries = []; | |
1496 emitLibraries(libraries); | |
1497 emitTypedefs(); | |
1498 emitMangledNames(); | |
1499 | |
1500 checkEverythingEmitted(elementDescriptors.keys); | |
1501 | |
1502 CodeBuffer libraryBuffer = new CodeBuffer(); | |
1503 for (LibraryElement library in Elements.sortedByPosition(libraries)) { | |
1504 writeLibraryDescriptors(libraryBuffer, library); | |
1505 elementDescriptors.remove(library); | |
1506 } | |
1507 | |
1508 mainBuffer | |
1509 ..write('(') | |
1510 ..write( | |
1511 jsAst.prettyPrint( | |
1512 getReflectionDataParser(classesCollector, backend), | |
1513 compiler)) | |
1514 ..write(')') | |
1515 ..write('([$n') | |
1516 ..add(libraryBuffer) | |
1517 ..write('])$N'); | |
1518 | |
1519 emitFinishClassesInvocationIfNecessary(mainBuffer); | |
1520 } | |
1521 | |
1522 typeTestEmitter.emitRuntimeTypeSupport(mainBuffer, mainOutputUnit); | |
1523 interceptorEmitter.emitGetInterceptorMethods(mainBuffer); | |
1524 interceptorEmitter.emitOneShotInterceptors(mainBuffer); | |
1525 | |
1526 if (task.outputContainsConstantList) { | |
1527 emitMakeConstantList(mainBuffer); | |
1528 } | |
1529 | |
1530 // Constants in checked mode call into RTI code to set type information | |
1531 // which may need getInterceptor (and one-shot interceptor) methods, so | |
1532 // we have to make sure that [emitGetInterceptorMethods] and | |
1533 // [emitOneShotInterceptors] have been called. | |
1534 emitCompileTimeConstants(mainBuffer, mainOutputUnit); | |
1535 | |
1536 emitDeferredBoilerPlate(mainBuffer, deferredLoadHashes); | |
1537 | |
1538 // Static field initializations require the classes and compile-time | |
1539 // constants to be set up. | |
1540 emitStaticNonFinalFieldInitializations(mainBuffer); | |
1541 interceptorEmitter.emitInterceptedNames(mainBuffer); | |
1542 interceptorEmitter.emitMapTypeToInterceptor(mainBuffer); | |
1543 emitLazilyInitializedStaticFields(mainBuffer); | |
1544 | |
1545 mainBuffer.writeln(); | |
1546 mainBuffer.add(nativeBuffer); | |
1547 | |
1548 metadataEmitter.emitMetadata(mainBuffer); | |
1549 | |
1550 isolateProperties = isolatePropertiesName; | |
1551 // The following code should not use the short-hand for the | |
1552 // initialStatics. | |
1553 mainBuffer.add('${namer.currentIsolate}$_=${_}null$N'); | |
1554 | |
1555 emitFinishIsolateConstructorInvocation(mainBuffer); | |
1556 mainBuffer.add( | |
1557 '${namer.currentIsolate}$_=${_}new ${namer.isolateName}()$N'); | |
1558 | |
1559 emitConvertToFastObjectFunction(); | |
1560 for (String globalObject in Namer.reservedGlobalObjectNames) { | |
1561 mainBuffer.add('$globalObject = convertToFastObject($globalObject)$N'); | |
1562 } | |
1563 if (DEBUG_FAST_OBJECTS) { | |
1564 mainBuffer.add(r''' | |
1565 // The following only works on V8 when run with option | |
1566 // "--allow-natives-syntax". We use'new Function' because the | |
1567 // miniparser does not understand V8 native syntax. | |
1568 if (typeof print === "function") { | |
1569 var HasFastProperties = | |
1570 new Function("a", "return %HasFastProperties(a)"); | |
1571 print("Size of global helper object: " | |
1572 + String(Object.getOwnPropertyNames(H).length) | |
1573 + ", fast properties " + HasFastProperties(H)); | |
1574 print("Size of global platform object: " | |
1575 + String(Object.getOwnPropertyNames(P).length) | |
1576 + ", fast properties " + HasFastProperties(P)); | |
1577 print("Size of global dart:html object: " | |
1578 + String(Object.getOwnPropertyNames(W).length) | |
1579 + ", fast properties " + HasFastProperties(W)); | |
1580 print("Size of isolate properties object: " | |
1581 + String(Object.getOwnPropertyNames($).length) | |
1582 + ", fast properties " + HasFastProperties($)); | |
1583 print("Size of constant object: " | |
1584 + String(Object.getOwnPropertyNames(C).length) | |
1585 + ", fast properties " + HasFastProperties(C)); | |
1586 var names = Object.getOwnPropertyNames($); | |
1587 for (var i = 0; i < names.length; i++) { | |
1588 print("$." + names[i]); | |
1589 } | |
1590 } | |
1591 '''); | |
1592 for (String object in Namer.userGlobalObjects) { | |
1593 mainBuffer.add(''' | |
1594 if (typeof print === "function") { | |
1595 print("Size of $object: " | |
1596 + String(Object.getOwnPropertyNames($object).length) | |
1597 + ", fast properties " + HasFastProperties($object)); | |
1598 } | |
1599 '''); | |
1600 } | |
1601 } | |
1602 | |
1603 jsAst.FunctionDeclaration precompiledFunctionAst = | |
1604 buildCspPrecompiledFunctionFor(mainOutputUnit); | |
1605 emitInitFunction(mainBuffer); | |
1606 emitMain(mainBuffer); | |
1607 mainBuffer.add('})()\n'); | |
1608 | |
1609 if (compiler.useContentSecurityPolicy) { | |
1610 mainBuffer.write( | |
1611 jsAst.prettyPrint( | |
1612 precompiledFunctionAst, | |
1613 compiler, | |
1614 monitor: compiler.dumpInfoTask, | |
1615 allowVariableMinification: false).getText()); | |
1616 } | |
1617 | |
1618 String assembledCode = mainBuffer.getText(); | |
1619 if (generateSourceMap) { | |
1620 outputSourceMap(assembledCode, mainBuffer, '', | |
1621 compiler.sourceMapUri, compiler.outputUri); | |
1622 mainBuffer.add( | |
1623 generateSourceMapTag(compiler.sourceMapUri, compiler.outputUri)); | |
1624 assembledCode = mainBuffer.getText(); | |
1625 } | |
1626 | |
1627 compiler.outputProvider('', 'js') | |
1628 ..add(assembledCode) | |
1629 ..close(); | |
1630 compiler.assembledCode = assembledCode; | |
1631 | |
1632 if (!compiler.useContentSecurityPolicy) { | |
1633 CodeBuffer cspBuffer = new CodeBuffer(); | |
1634 cspBuffer.add(mainBuffer); | |
1635 cspBuffer.write(""" | |
1636 { | |
1637 var message = | |
1638 'Deprecation: Automatic generation of output for Content Security\\n' + | |
1639 'Policy is deprecated and will be removed with the next development\\n' + | |
1640 'release. Use the --csp option to generate CSP restricted output.'; | |
1641 if (typeof dartPrint == "function") { | |
1642 dartPrint(message); | |
1643 } else if (typeof console == "object" && typeof console.log == "function") { | |
1644 console.log(message); | |
1645 } else if (typeof print == "function") { | |
1646 print(message); | |
1647 } | |
1648 }\n"""); | |
1649 | |
1650 cspBuffer.write( | |
1651 jsAst.prettyPrint( | |
1652 precompiledFunctionAst, compiler, | |
1653 allowVariableMinification: false).getText()); | |
1654 | |
1655 compiler.outputProvider('', 'precompiled.js') | |
1656 ..add(cspBuffer.getText()) | |
1657 ..close(); | |
1658 } | |
1659 } | |
1660 | |
1661 /// Returns a map from OutputUnit to a hash of its content. The hash uniquely | |
1662 /// identifies the code of the output-unit. It does not include | |
1663 /// boilerplate JS code, like the sourcemap directives or the hash | |
1664 /// itself. | |
1665 Map<OutputUnit, String> emitDeferredOutputUnits() { | |
1666 if (!compiler.deferredLoadTask.isProgramSplit) return const {}; | |
1667 | |
1668 Map<OutputUnit, CodeBuffer> outputBuffers = | |
1669 new Map<OutputUnit, CodeBuffer>(); | |
1670 | |
1671 for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) { | |
1672 if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) continue; | |
1673 | |
1674 List<Element> functions = task.outputStaticLists[outputUnit]; | |
1675 if (functions != null) { | |
1676 emitStaticFunctions(functions); | |
1677 } | |
1678 | |
1679 List<ClassElement> classes = task.outputClassLists[outputUnit]; | |
1680 if (classes != null) { | |
1681 for (ClassElement element in classes) { | |
1682 generateClass(element, getElementDescriptor(element)); | |
1683 } | |
1684 } | |
1685 | |
1686 if (elementDescriptors.isNotEmpty) { | |
1687 Iterable<LibraryElement> libraries = | |
1688 task.outputLibraryLists[outputUnit]; | |
1689 if (libraries == null) libraries = []; | |
1690 emitLibraries(libraries); | |
1691 | |
1692 CodeBuffer buffer = new CodeBuffer(); | |
1693 outputBuffers[outputUnit] = buffer; | |
1694 for (LibraryElement library in Elements.sortedByPosition(libraries)) { | |
1695 writeLibraryDescriptors(buffer, library); | |
1696 elementDescriptors.remove(library); | |
1697 } | |
1698 } | |
1699 } | |
1700 | |
1701 return emitDeferredCode(outputBuffers); | |
1702 } | |
1703 | |
1704 CodeBuffer buildNativesBuffer() { | |
1705 // Emit native classes on [nativeBuffer]. | |
1706 final CodeBuffer nativeBuffer = new CodeBuffer(); | |
1707 | |
1708 if (nativeClasses.isEmpty) return nativeBuffer; | |
1709 | |
1710 | |
1711 addComment('Native classes', nativeBuffer); | |
1712 | |
1713 nativeEmitter.generateNativeClasses(nativeClasses, mainBuffer, | |
1714 additionalProperties); | |
1715 | |
1716 nativeEmitter.finishGenerateNativeClasses(); | |
1717 nativeEmitter.assembleCode(nativeBuffer); | |
1718 | |
1719 return nativeBuffer; | |
1720 } | |
1721 | |
1722 void emitProgram(Program program) { | |
1723 // Shorten the code by using [namer.currentIsolate] as temporary. | |
1724 isolateProperties = namer.currentIsolate; | |
1725 | |
1726 classesCollector = r"$$"; | |
1727 | |
1728 // Emit deferred units first, so we have their hashes. | |
1729 // Map from OutputUnit to a hash of its content. The hash uniquely | |
1730 // identifies the code of the output-unit. It does not include | |
1731 // boilerplate JS code, like the sourcemap directives or the hash | |
1732 // itself. | |
1733 Map<OutputUnit, String> deferredLoadHashes = emitDeferredOutputUnits(); | |
1734 CodeBuffer nativeBuffer = buildNativesBuffer(); | |
1735 emitMainOutputUnit(deferredLoadHashes, nativeBuffer); | |
1736 | |
1737 if (backend.requiresPreamble && | |
1738 !backend.htmlLibraryIsLoaded) { | |
1739 compiler.reportHint(NO_LOCATION_SPANNABLE, MessageKind.PREAMBLE); | |
1740 } | |
1741 } | |
1742 | |
1743 String generateSourceMapTag(Uri sourceMapUri, Uri fileUri) { | |
1744 if (sourceMapUri != null && fileUri != null) { | |
1745 String sourceMapFileName = relativize(fileUri, sourceMapUri, false); | |
1746 return ''' | |
1747 | |
1748 //# sourceMappingURL=$sourceMapFileName | |
1749 '''; | |
1750 } | |
1751 return ''; | |
1752 } | |
1753 | |
1754 ClassBuilder getElementDescriptor(Element element) { | |
1755 Element owner = element.library; | |
1756 if (!element.isLibrary && !element.isTopLevel && !element.isNative) { | |
1757 // For static (not top level) elements, record their code in a buffer | |
1758 // specific to the class. For now, not supported for native classes and | |
1759 // native elements. | |
1760 ClassElement cls = | |
1761 element.enclosingClassOrCompilationUnit.declaration; | |
1762 if (compiler.codegenWorld.directlyInstantiatedClasses.contains(cls) | |
1763 && !cls.isNative) { | |
1764 owner = cls; | |
1765 } | |
1766 } | |
1767 if (owner == null) { | |
1768 compiler.internalError(element, 'Owner is null.'); | |
1769 } | |
1770 return elementDescriptors.putIfAbsent( | |
1771 owner, | |
1772 () => new ClassBuilder(owner, namer)); | |
1773 } | |
1774 | |
1775 /// Emits support-code for deferred loading into [buffer]. | |
1776 void emitDeferredBoilerPlate(CodeBuffer buffer, | |
1777 Map<OutputUnit, String> deferredLoadHashes) { | |
1778 // Function for checking if a hunk is loaded given its hash. | |
1779 buffer.write(jsAst.prettyPrint( | |
1780 js('# = function(hunkHash) {' | |
1781 ' return !!$deferredInitializers[hunkHash];' | |
1782 '}', generateEmbeddedGlobalAccess(embeddedNames.IS_HUNK_LOADED)), | |
1783 compiler, monitor: compiler.dumpInfoTask)); | |
1784 buffer.write('$N'); | |
1785 // Function for initializing a loaded hunk, given its hash. | |
1786 buffer.write(jsAst.prettyPrint( | |
1787 js('# = function(hunkHash) {' | |
1788 ' $deferredInitializers[hunkHash](' | |
1789 '$globalsHolder, ${namer.currentIsolate})' | |
1790 '}', | |
1791 generateEmbeddedGlobalAccess( | |
1792 embeddedNames.INITIALIZE_LOADED_HUNK)), | |
1793 compiler, monitor: compiler.dumpInfoTask)); | |
1794 buffer.write('$N'); | |
1795 // Write a javascript mapping from Deferred import load ids (derrived | |
1796 // from the import prefix.) to a list of lists of uris of hunks to load, | |
1797 // and a corresponding mapping to a list of hashes used by | |
1798 // INITIALIZE_LOADED_HUNK and IS_HUNK_LOADED. | |
1799 Map<String, List<String>> deferredLibraryUris = | |
1800 new Map<String, List<String>>(); | |
1801 Map<String, List<String>> deferredLibraryHashes = | |
1802 new Map<String, List<String>>(); | |
1803 compiler.deferredLoadTask.hunksToLoad.forEach( | |
1804 (String loadId, List<OutputUnit>outputUnits) { | |
1805 List<String> uris = new List<String>(); | |
1806 List<String> hashes = new List<String>(); | |
1807 deferredLibraryHashes[loadId] = new List<String>(); | |
1808 for (OutputUnit outputUnit in outputUnits) { | |
1809 uris.add(deferredPartFileName(outputUnit)); | |
1810 hashes.add(deferredLoadHashes[outputUnit]); | |
1811 } | |
1812 | |
1813 deferredLibraryUris[loadId] = uris; | |
1814 deferredLibraryHashes[loadId] = hashes; | |
1815 }); | |
1816 | |
1817 void emitMapping(String name, Map<String, List<String>> mapping) { | |
1818 List<jsAst.Property> properties = new List<jsAst.Property>(); | |
1819 mapping.forEach((String key, List<String> values) { | |
1820 properties.add(new jsAst.Property(js.escapedString(key), | |
1821 new jsAst.ArrayInitializer.from( | |
1822 values.map(js.escapedString)))); | |
1823 }); | |
1824 jsAst.Node initializer = | |
1825 new jsAst.ObjectInitializer(properties, isOneLiner: true); | |
1826 | |
1827 jsAst.Node globalName = generateEmbeddedGlobalAccess(name); | |
1828 buffer.write(jsAst.prettyPrint( | |
1829 js("# = #", [globalName, initializer]), | |
1830 compiler, monitor: compiler.dumpInfoTask)); | |
1831 buffer.write('$N'); | |
1832 | |
1833 } | |
1834 | |
1835 emitMapping(embeddedNames.DEFERRED_LIBRARY_URIS, deferredLibraryUris); | |
1836 emitMapping(embeddedNames.DEFERRED_LIBRARY_HASHES, | |
1837 deferredLibraryHashes); | |
1838 } | |
1839 | |
1840 /// Emits code for all output units except the main. | |
1841 /// Returns a mapping from outputUnit to a hash of the corresponding hunk that | |
1842 /// can be used for calling the initializer. | |
1843 Map<OutputUnit, String> emitDeferredCode( | |
1844 Map<OutputUnit, CodeBuffer> deferredBuffers) { | |
1845 | |
1846 Map<OutputUnit, String> hunkHashes = new Map<OutputUnit, String>(); | |
1847 | |
1848 for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) { | |
1849 if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) continue; | |
1850 | |
1851 CodeBuffer libraryDescriptorBuffer = deferredBuffers[outputUnit]; | |
1852 | |
1853 CodeBuffer outputBuffer = new CodeBuffer(); | |
1854 | |
1855 outputBuffer..write(buildGeneratedBy()) | |
1856 ..write('${deferredInitializers}.current$_=$_' | |
1857 'function$_(${globalsHolder}) {$N'); | |
1858 for (String globalObject in Namer.reservedGlobalObjectNames) { | |
1859 outputBuffer | |
1860 .write('var $globalObject$_=$_' | |
1861 '${globalsHolder}.$globalObject$N'); | |
1862 } | |
1863 outputBuffer | |
1864 ..write('var init$_=$_${globalsHolder}.init$N') | |
1865 ..write('var ${namer.isolateName}$_=$_' | |
1866 '${globalsHolder}.${namer.isolateName}$N'); | |
1867 if (libraryDescriptorBuffer != null) { | |
1868 // TODO(ahe): This defines a lot of properties on the | |
1869 // Isolate.prototype object. We know this will turn it into a | |
1870 // slow object in V8, so instead we should do something similar | |
1871 // to Isolate.$finishIsolateConstructor. | |
1872 outputBuffer | |
1873 ..write('var ${namer.currentIsolate}$_=$_$isolatePropertiesName$N') | |
1874 // The classesCollector object ($$). | |
1875 ..write('$classesCollector$_=${_}Object.create(null);$n') | |
1876 ..write('(') | |
1877 ..write( | |
1878 jsAst.prettyPrint( | |
1879 getReflectionDataParser(classesCollector, backend), | |
1880 compiler, monitor: compiler.dumpInfoTask)) | |
1881 ..write(')') | |
1882 ..write('([$n') | |
1883 ..addBuffer(libraryDescriptorBuffer) | |
1884 ..write('])$N'); | |
1885 | |
1886 if (outputClassLists.containsKey(outputUnit)) { | |
1887 outputBuffer.write( | |
1888 '$finishClassesName($classesCollector,$_${namer.currentIsolate},' | |
1889 '$_$isolatePropertiesName)$N'); | |
1890 } | |
1891 | |
1892 } | |
1893 | |
1894 // Set the currentIsolate variable to the current isolate (which is | |
1895 // provided as second argument). | |
1896 // We need to do this, because we use the same variable for setting up | |
1897 // the isolate-properties and for storing the current isolate. During | |
1898 // the setup (the code above this lines) we must set the variable to | |
1899 // the isolate-properties. | |
1900 // After we have done the setup (finishing with `finishClasses`) it must | |
1901 // point to the current Isolate. Otherwise all methods/functions | |
1902 // accessing isolate variables will access the wrong object. | |
1903 outputBuffer.write("${namer.currentIsolate}$_=${_}arguments[1]$N"); | |
1904 typeTestEmitter.emitRuntimeTypeSupport(outputBuffer, outputUnit); | |
1905 | |
1906 emitCompileTimeConstants(outputBuffer, outputUnit); | |
1907 outputBuffer.write('}$N'); | |
1908 | |
1909 if (compiler.useContentSecurityPolicy) { | |
1910 jsAst.FunctionDeclaration precompiledFunctionAst = | |
1911 buildCspPrecompiledFunctionFor(outputUnit); | |
1912 | |
1913 outputBuffer.write( | |
1914 jsAst.prettyPrint( | |
1915 precompiledFunctionAst, compiler, | |
1916 monitor: compiler.dumpInfoTask, | |
1917 allowVariableMinification: false).getText()); | |
1918 } | |
1919 | |
1920 // Make a unique hash of the code (before the sourcemaps are added) | |
1921 // This will be used to retrieve the initializing function from the global | |
1922 // variable. | |
1923 String hash = hashOfString(outputBuffer.getText()); | |
1924 | |
1925 outputBuffer.add('${deferredInitializers}["$hash"]$_=$_' | |
1926 '${deferredInitializers}.current$N'); | |
1927 | |
1928 String partPrefix = deferredPartFileName(outputUnit, addExtension: false); | |
1929 if (generateSourceMap) { | |
1930 Uri mapUri, partUri; | |
1931 Uri sourceMapUri = compiler.sourceMapUri; | |
1932 Uri outputUri = compiler.outputUri; | |
1933 | |
1934 String partName = "$partPrefix.part"; | |
1935 | |
1936 if (sourceMapUri != null) { | |
1937 String mapFileName = partName + ".js.map"; | |
1938 List<String> mapSegments = sourceMapUri.pathSegments.toList(); | |
1939 mapSegments[mapSegments.length - 1] = mapFileName; | |
1940 mapUri = compiler.sourceMapUri.replace(pathSegments: mapSegments); | |
1941 } | |
1942 | |
1943 if (outputUri != null) { | |
1944 String partFileName = partName + ".js"; | |
1945 List<String> partSegments = outputUri.pathSegments.toList(); | |
1946 partSegments[partSegments.length - 1] = partFileName; | |
1947 partUri = compiler.outputUri.replace(pathSegments: partSegments); | |
1948 } | |
1949 | |
1950 outputSourceMap(outputBuffer.getText(), outputBuffer, partName, | |
1951 mapUri, partUri); | |
1952 outputBuffer.add(generateSourceMapTag(mapUri, partUri)); | |
1953 } | |
1954 | |
1955 outputBuffers[outputUnit] = outputBuffer; | |
1956 compiler.outputProvider(partPrefix, 'part.js') | |
1957 ..add(outputBuffer.getText()) | |
1958 ..close(); | |
1959 | |
1960 hunkHashes[outputUnit] = hash; | |
1961 } | |
1962 return hunkHashes; | |
1963 } | |
1964 | |
1965 String buildGeneratedBy() { | |
1966 var suffix = ''; | |
1967 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}'; | |
1968 return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n'; | |
1969 } | |
1970 | |
1971 void outputSourceMap(String code, CodeBuffer buffer, String name, | |
1972 [Uri sourceMapUri, Uri fileUri]) { | |
1973 if (!generateSourceMap) return; | |
1974 // Create a source file for the compilation output. This allows using | |
1975 // [:getLine:] to transform offsets to line numbers in [SourceMapBuilder]. | |
1976 SourceFile compiledFile = new StringSourceFile(null, code); | |
1977 SourceMapBuilder sourceMapBuilder = | |
1978 new SourceMapBuilder(sourceMapUri, fileUri, compiledFile); | |
1979 buffer.forEachSourceLocation(sourceMapBuilder.addMapping); | |
1980 String sourceMap = sourceMapBuilder.build(); | |
1981 compiler.outputProvider(name, 'js.map') | |
1982 ..add(sourceMap) | |
1983 ..close(); | |
1984 } | |
1985 | |
1986 void invalidateCaches() { | |
1987 if (!compiler.hasIncrementalSupport) return; | |
1988 if (cachedElements.isEmpty) return; | |
1989 for (Element element in compiler.enqueuer.codegen.newlyEnqueuedElements) { | |
1990 if (element.isInstanceMember) { | |
1991 cachedClassBuilders.remove(element.enclosingClass); | |
1992 | |
1993 nativeEmitter.cachedBuilders.remove(element.enclosingClass); | |
1994 | |
1995 } | |
1996 } | |
1997 } | |
1998 } | |
OLD | NEW |