Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(741)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_emitter/old_emitter/emitter.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698