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

Side by Side Diff: pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart

Issue 1220333003: dart2js: Rename emitters. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 5 months 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
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 final InterceptorEmitter interceptorEmitter = new InterceptorEmitter();
16
17 // TODO(johnniwinther): Wrap these fields in a caching strategy.
18 final Set<ConstantValue> cachedEmittedConstants;
19 final List<jsAst.Statement> cachedEmittedConstantsAst = <jsAst.Statement>[];
20 final Map<Element, ClassBuilder> cachedClassBuilders;
21 final Set<Element> cachedElements;
22
23 bool needsClassSupport = false;
24 bool needsMixinSupport = false;
25 bool needsLazyInitializer = false;
26
27 /// True if [ContainerBuilder.addMemberMethodFromInfo] used "structured info",
28 /// that is, some function was needed for reflection, had stubs, or had a
29 /// super alias.
30 bool needsStructuredMemberInfo = false;
31
32 final Namer namer;
33 ConstantEmitter constantEmitter;
34 NativeEmitter get nativeEmitter => task.nativeEmitter;
35 TypeTestRegistry get typeTestRegistry => task.typeTestRegistry;
36
37 // The full code that is written to each hunk part-file.
38 Map<OutputUnit, CodeOutput> outputBuffers = new Map<OutputUnit, CodeOutput>();
39
40 String classesCollector;
41 Set<ClassElement> get neededClasses => task.neededClasses;
42 Map<OutputUnit, List<ClassElement>> get outputClassLists
43 => task.outputClassLists;
44 Map<OutputUnit, List<ConstantValue>> get outputConstantLists
45 => task.outputConstantLists;
46 final Map<jsAst.Name, String> mangledFieldNames =
47 new HashMap<jsAst.Name, String>();
48 final Map<jsAst.Name, String> mangledGlobalFieldNames =
49 new HashMap<jsAst.Name, String>();
50 final Set<jsAst.Name> recordedMangledNames = new Set<jsAst.Name>();
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 /**
64 * List of expressions and statements that will be included in the
65 * precompiled function.
66 *
67 * To save space, dart2js normally generates constructors and accessors
68 * dynamically. This doesn't work in CSP mode, so dart2js emits them directly
69 * when in CSP mode.
70 */
71 Map<OutputUnit, List<jsAst.Node>> _cspPrecompiledFunctions =
72 new Map<OutputUnit, List<jsAst.Node>>();
73
74 Map<OutputUnit, List<jsAst.Expression>> _cspPrecompiledConstructorNames =
75 new Map<OutputUnit, List<jsAst.Expression>>();
76
77 /**
78 * Accumulate properties for classes and libraries, describing their
79 * static/top-level members.
80 * Later, these members are emitted when the class or library is emitted.
81 *
82 * See [getElementDescriptor].
83 */
84 // TODO(ahe): Generate statics with their class, and store only libraries in
85 // this map.
86 final Map<Fragment, Map<Element, ClassBuilder>> elementDescriptors =
87 new Map<Fragment, Map<Element, ClassBuilder>>();
88
89 final bool generateSourceMap;
90
91 OldEmitter(Compiler compiler, Namer namer, this.generateSourceMap, this.task)
92 : this.compiler = compiler,
93 this.namer = namer,
94 cachedEmittedConstants = compiler.cacheStrategy.newSet(),
95 cachedClassBuilders = compiler.cacheStrategy.newMap(),
96 cachedElements = compiler.cacheStrategy.newSet() {
97 constantEmitter = new ConstantEmitter(
98 compiler, namer, this.constantReference, constantListGenerator);
99 containerBuilder.emitter = this;
100 classEmitter.emitter = this;
101 nsmEmitter.emitter = this;
102 interceptorEmitter.emitter = this;
103 }
104
105 List<jsAst.Node> cspPrecompiledFunctionFor(OutputUnit outputUnit) {
106 return _cspPrecompiledFunctions.putIfAbsent(
107 outputUnit,
108 () => new List<jsAst.Node>());
109 }
110
111 List<jsAst.Expression> cspPrecompiledConstructorNamesFor(
112 OutputUnit outputUnit) {
113 return _cspPrecompiledConstructorNames.putIfAbsent(
114 outputUnit,
115 () => new List<jsAst.Expression>());
116 }
117
118 /// Erases the precompiled information for csp mode for all output units.
119 /// Used by the incremental compiler.
120 void clearCspPrecompiledNodes() {
121 _cspPrecompiledFunctions.clear();
122 _cspPrecompiledConstructorNames.clear();
123 }
124
125 @override
126 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) {
127 if (constant.isFunction) return true; // Already emitted.
128 if (constant.isPrimitive) return true; // Inlined.
129 if (constant.isDummy) return true; // Inlined.
130 // The name is null when the constant is already a JS constant.
131 // TODO(floitsch): every constant should be registered, so that we can
132 // share the ones that take up too much space (like some strings).
133 if (namer.constantName(constant) == null) return true;
134 return false;
135 }
136
137 @override
138 int compareConstants(ConstantValue a, ConstantValue b) {
139 // Inlined constants don't affect the order and sometimes don't even have
140 // names.
141 int cmp1 = isConstantInlinedOrAlreadyEmitted(a) ? 0 : 1;
142 int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1;
143 if (cmp1 + cmp2 < 2) return cmp1 - cmp2;
144
145 // Emit constant interceptors first. Constant interceptors for primitives
146 // might be used by code that builds other constants. See Issue 18173.
147 if (a.isInterceptor != b.isInterceptor) {
148 return a.isInterceptor ? -1 : 1;
149 }
150
151 // Sorting by the long name clusters constants with the same constructor
152 // which compresses a tiny bit better.
153 int r = namer.constantLongName(a).compareTo(namer.constantLongName(b));
154 if (r != 0) return r;
155 // Resolve collisions in the long name by using the constant name (i.e. JS
156 // name) which is unique.
157 // TODO(herhut): Find a better way to resolve collisions.
158 return namer.constantName(a).hashCode.compareTo(
159 namer.constantName(b).hashCode);
160 }
161
162 @override
163 jsAst.Expression constantReference(ConstantValue value) {
164 if (value.isFunction) {
165 FunctionConstantValue functionConstant = value;
166 return isolateStaticClosureAccess(functionConstant.element);
167 }
168
169 // We are only interested in the "isInlined" part, but it does not hurt to
170 // test for the other predicates.
171 if (isConstantInlinedOrAlreadyEmitted(value)) {
172 return constantEmitter.generate(value);
173 }
174 return js('#.#', [namer.globalObjectForConstant(value),
175 namer.constantName(value)]);
176 }
177
178 jsAst.Expression constantInitializerExpression(ConstantValue value) {
179 return constantEmitter.generate(value);
180 }
181
182 String get name => 'CodeEmitter';
183
184 String get finishIsolateConstructorName
185 => '${namer.isolateName}.\$finishIsolateConstructor';
186 String get isolatePropertiesName
187 => '${namer.isolateName}.${namer.isolatePropertiesName}';
188 String get lazyInitializerProperty
189 => r'$lazy';
190 String get lazyInitializerName
191 => '${namer.isolateName}.${lazyInitializerProperty}';
192 String get initName => 'init';
193
194 jsAst.Name get makeConstListProperty
195 => namer.internalGlobal('makeConstantList');
196
197 /// The name of the property that contains all field names.
198 ///
199 /// This property is added to constructors when isolate support is enabled.
200 static const String FIELD_NAMES_PROPERTY_NAME = r"$__fields__";
201
202 /// For deferred loading we communicate the initializers via this global var.
203 final String deferredInitializers = r"$dart_deferred_initializers$";
204
205 /// Contains the global state that is needed to initialize and load a
206 /// deferred library.
207 String get globalsHolder => r"$globals$";
208
209 @override
210 jsAst.Expression generateEmbeddedGlobalAccess(String global) {
211 return js(generateEmbeddedGlobalAccessString(global));
212 }
213
214 String generateEmbeddedGlobalAccessString(String global) {
215 // TODO(floitsch): don't use 'init' as global embedder storage.
216 return '$initName.$global';
217 }
218
219 jsAst.PropertyAccess globalPropertyAccess(Element element) {
220 jsAst.Name name = namer.globalPropertyName(element);
221 jsAst.PropertyAccess pa = new jsAst.PropertyAccess(
222 new jsAst.VariableUse(namer.globalObjectFor(element)),
223 name);
224 return pa;
225 }
226
227 @override
228 jsAst.Expression isolateLazyInitializerAccess(FieldElement element) {
229 return jsAst.js('#.#', [namer.globalObjectFor(element),
230 namer.lazyInitializerName(element)]);
231 }
232
233 @override
234 jsAst.Expression isolateStaticClosureAccess(FunctionElement element) {
235 return jsAst.js('#.#()',
236 [namer.globalObjectFor(element), namer.staticClosureName(element)]);
237 }
238
239 @override
240 jsAst.PropertyAccess staticFieldAccess(FieldElement element) {
241 return globalPropertyAccess(element);
242 }
243
244 @override
245 jsAst.PropertyAccess staticFunctionAccess(FunctionElement element) {
246 return globalPropertyAccess(element);
247 }
248
249 @override
250 jsAst.PropertyAccess constructorAccess(ClassElement element) {
251 return globalPropertyAccess(element);
252 }
253
254 @override
255 jsAst.PropertyAccess prototypeAccess(ClassElement element,
256 bool hasBeenInstantiated) {
257 return jsAst.js('#.prototype', constructorAccess(element));
258 }
259
260 @override
261 jsAst.PropertyAccess interceptorClassAccess(ClassElement element) {
262 return globalPropertyAccess(element);
263 }
264
265 @override
266 jsAst.PropertyAccess typeAccess(Element element) {
267 return globalPropertyAccess(element);
268 }
269
270 @override
271 jsAst.Template templateForBuiltin(JsBuiltin builtin) {
272 switch (builtin) {
273 case JsBuiltin.dartObjectConstructor:
274 return jsAst.js.expressionTemplateYielding(
275 typeAccess(compiler.objectClass));
276
277 case JsBuiltin.isCheckPropertyToJsConstructorName:
278 int isPrefixLength = namer.operatorIsPrefix.length;
279 return jsAst.js.expressionTemplateFor('#.substring($isPrefixLength)');
280
281 case JsBuiltin.isFunctionType:
282 return backend.rti.representationGenerator.templateForIsFunctionType;
283
284 case JsBuiltin.rawRtiToJsConstructorName:
285 return jsAst.js.expressionTemplateFor("#.$typeNameProperty");
286
287 case JsBuiltin.rawRuntimeType:
288 return jsAst.js.expressionTemplateFor("#.constructor");
289
290 case JsBuiltin.createFunctionTypeRti:
291 return backend.rti.representationGenerator
292 .templateForCreateFunctionType;
293
294 case JsBuiltin.isSubtype:
295 // TODO(floitsch): move this closer to where is-check properties are
296 // built.
297 String isPrefix = namer.operatorIsPrefix;
298 return jsAst.js.expressionTemplateFor(
299 "('$isPrefix' + #) in #.prototype");
300
301 case JsBuiltin.isGivenTypeRti:
302 return jsAst.js.expressionTemplateFor('#.$typeNameProperty === #');
303
304 case JsBuiltin.getMetadata:
305 String metadataAccess =
306 generateEmbeddedGlobalAccessString(embeddedNames.METADATA);
307 return jsAst.js.expressionTemplateFor("$metadataAccess[#]");
308
309 case JsBuiltin.getType:
310 String typesAccess =
311 generateEmbeddedGlobalAccessString(embeddedNames.TYPES);
312 return jsAst.js.expressionTemplateFor("$typesAccess[#]");
313
314 default:
315 compiler.internalError(NO_LOCATION_SPANNABLE,
316 "Unhandled Builtin: $builtin");
317 return null;
318 }
319 }
320
321 List<jsAst.Statement> buildTrivialNsmHandlers(){
322 return nsmEmitter.buildTrivialNsmHandlers();
323 }
324
325 jsAst.Statement buildNativeInfoHandler(
326 jsAst.Expression infoAccess,
327 jsAst.Expression constructorAccess,
328 jsAst.Expression subclassReadGenerator(jsAst.Expression subclass),
329 jsAst.Expression interceptorsByTagAccess,
330 jsAst.Expression leafTagsAccess) {
331 return NativeGenerator.buildNativeInfoHandler(infoAccess, constructorAccess,
332 subclassReadGenerator,
333 interceptorsByTagAccess,
334 leafTagsAccess);
335 }
336
337 jsAst.ObjectInitializer generateInterceptedNamesSet() {
338 return interceptorEmitter.generateInterceptedNamesSet();
339 }
340
341 /// In minified mode we want to keep the name for the most common core types.
342 bool _isNativeTypeNeedingReflectionName(Element element) {
343 if (!element.isClass) return false;
344 return (element == compiler.intClass ||
345 element == compiler.doubleClass ||
346 element == compiler.numClass ||
347 element == compiler.stringClass ||
348 element == compiler.boolClass ||
349 element == compiler.nullClass ||
350 element == compiler.listClass);
351 }
352
353 /// Returns the "reflection name" of an [Element] or [Selector].
354 /// The reflection name of a getter 'foo' is 'foo'.
355 /// The reflection name of a setter 'foo' is 'foo='.
356 /// The reflection name of a method 'foo' is 'foo:N:M:O', where N is the
357 /// number of required arguments, M is the number of optional arguments, and
358 /// O is the named arguments.
359 /// The reflection name of a constructor is similar to a regular method but
360 /// starts with 'new '.
361 /// The reflection name of class 'C' is 'C'.
362 /// An anonymous mixin application has no reflection name.
363 /// This is used by js_mirrors.dart.
364 String getReflectionName(elementOrSelector, jsAst.Name mangledName) {
365 String name = elementOrSelector.name;
366 if (backend.shouldRetainName(name) ||
367 elementOrSelector is Element &&
368 // Make sure to retain names of unnamed constructors, and
369 // for common native types.
370 ((name == '' &&
371 backend.isAccessibleByReflection(elementOrSelector)) ||
372 _isNativeTypeNeedingReflectionName(elementOrSelector))) {
373
374 // TODO(ahe): Enable the next line when I can tell the difference between
375 // an instance method and a global. They may have the same mangled name.
376 // if (recordedMangledNames.contains(mangledName)) return null;
377 recordedMangledNames.add(mangledName);
378 return getReflectionNameInternal(elementOrSelector, mangledName);
379 }
380 return null;
381 }
382
383 String getReflectionNameInternal(elementOrSelector,
384 jsAst.Name mangledName) {
385 String name = namer.privateName(elementOrSelector.memberName);
386 if (elementOrSelector.isGetter) return name;
387 if (elementOrSelector.isSetter) {
388 if (mangledName is! SetterName) return '$name=';
389 SetterName setterName = mangledName;
390 jsAst.Name base = setterName.base;
391 jsAst.Name getter = namer.deriveGetterName(base);
392 mangledFieldNames.putIfAbsent(getter, () => name);
393 assert(mangledFieldNames[getter] == name);
394 recordedMangledNames.add(getter);
395 // TODO(karlklose,ahe): we do not actually need to store information
396 // about the name of this setter in the output, but it is needed for
397 // marking the function as invokable by reflection.
398 return '$name=';
399 }
400 if (elementOrSelector is Element && elementOrSelector.isClosure) {
401 // Closures are synthesized and their name might conflict with existing
402 // globals. Assign an illegal name, and make sure they don't clash
403 // with each other.
404 return " $name";
405 }
406 if (elementOrSelector is Selector
407 || elementOrSelector.isFunction
408 || elementOrSelector.isConstructor) {
409 int positionalParameterCount;
410 String namedArguments = '';
411 bool isConstructor = false;
412 if (elementOrSelector is Selector) {
413 CallStructure callStructure = elementOrSelector.callStructure;
414 positionalParameterCount = callStructure.positionalArgumentCount;
415 namedArguments = namedParametersAsReflectionNames(callStructure);
416 } else {
417 FunctionElement function = elementOrSelector;
418 if (function.isConstructor) {
419 isConstructor = true;
420 name = Elements.reconstructConstructorName(function);
421 }
422 FunctionSignature signature = function.functionSignature;
423 positionalParameterCount = signature.requiredParameterCount;
424 if (signature.optionalParametersAreNamed) {
425 var names = [];
426 for (Element e in signature.optionalParameters) {
427 names.add(e.name);
428 }
429 CallStructure callStructure =
430 new CallStructure(positionalParameterCount, names);
431 namedArguments = namedParametersAsReflectionNames(callStructure);
432 } else {
433 // Named parameters are handled differently by mirrors. For unnamed
434 // parameters, they are actually required if invoked
435 // reflectively. Also, if you have a method c(x) and c([x]) they both
436 // get the same mangled name, so they must have the same reflection
437 // name.
438 positionalParameterCount += signature.optionalParameterCount;
439 }
440 }
441 String suffix = '$name:$positionalParameterCount$namedArguments';
442 return (isConstructor) ? 'new $suffix' : suffix;
443 }
444 Element element = elementOrSelector;
445 if (element.isGenerativeConstructorBody) {
446 return null;
447 } else if (element.isClass) {
448 ClassElement cls = element;
449 if (cls.isUnnamedMixinApplication) return null;
450 return cls.name;
451 } else if (element.isTypedef) {
452 return element.name;
453 }
454 throw compiler.internalError(element,
455 'Do not know how to reflect on this $element.');
456 }
457
458 String namedParametersAsReflectionNames(CallStructure structure) {
459 if (structure.isUnnamed) return '';
460 String names = structure.getOrderedNamedArguments().join(':');
461 return ':$names';
462 }
463
464 jsAst.Statement buildCspPrecompiledFunctionFor(
465 OutputUnit outputUnit) {
466 if (compiler.useContentSecurityPolicy) {
467 // TODO(ahe): Compute a hash code.
468 // TODO(sigurdm): Avoid this precompiled function. Generated
469 // constructor-functions and getter/setter functions can be stored in the
470 // library-description table. Setting properties on these can be moved to
471 // finishClasses.
472 return js.statement(r"""
473 #precompiled = function ($collectedClasses$) {
474 #norename;
475 var $desc;
476 #functions;
477 return #result;
478 };""",
479 {'norename': new jsAst.Comment("// ::norenaming:: "),
480 'precompiled': generateEmbeddedGlobalAccess(embeddedNames.PRECOMPILED),
481 'functions': cspPrecompiledFunctionFor(outputUnit),
482 'result': new jsAst.ArrayInitializer(
483 cspPrecompiledConstructorNamesFor(outputUnit))});
484 } else {
485 return js.comment("Constructors are generated at runtime.");
486 }
487 }
488
489 void assembleClass(Class cls, ClassBuilder enclosingBuilder,
490 Fragment fragment) {
491 ClassElement classElement = cls.element;
492 compiler.withCurrentElement(classElement, () {
493 if (compiler.hasIncrementalSupport) {
494 ClassBuilder cachedBuilder =
495 cachedClassBuilders.putIfAbsent(classElement, () {
496 ClassBuilder builder =
497 new ClassBuilder.forClass(classElement, namer);
498 classEmitter.emitClass(cls, builder, fragment);
499 return builder;
500 });
501 invariant(classElement, cachedBuilder.fields.isEmpty);
502 invariant(classElement, cachedBuilder.superName == null);
503 invariant(classElement, cachedBuilder.functionType == null);
504 invariant(classElement, cachedBuilder.fieldMetadata == null);
505 enclosingBuilder.properties.addAll(cachedBuilder.properties);
506 } else {
507 classEmitter.emitClass(cls, enclosingBuilder, fragment);
508 }
509 });
510 }
511
512 void assembleStaticFunctions(Iterable<Method> staticFunctions,
513 Fragment fragment) {
514 if (staticFunctions == null) return;
515
516 for (Method method in staticFunctions) {
517 Element element = method.element;
518 // We need to filter out null-elements for the interceptors.
519 // TODO(floitsch): use the precomputed interceptors here.
520 if (element == null) continue;
521 ClassBuilder builder = new ClassBuilder.forStatics(element, namer);
522 containerBuilder.addMemberMethod(method, builder);
523 getElementDescriptor(element, fragment).properties
524 .addAll(builder.properties);
525 }
526 }
527
528 jsAst.Statement buildStaticNonFinalFieldInitializations(
529 OutputUnit outputUnit) {
530 jsAst.Statement buildInitialization(Element element,
531 jsAst.Expression initialValue) {
532 // Note: `namer.currentIsolate` refers to the isolate properties here.
533 return js.statement('${namer.currentIsolate}.# = #',
534 [namer.globalPropertyName(element), initialValue]);
535 }
536
537 bool inMainUnit = (outputUnit == compiler.deferredLoadTask.mainOutputUnit);
538 JavaScriptConstantCompiler handler = backend.constants;
539 List<jsAst.Statement> parts = <jsAst.Statement>[];
540
541 Iterable<Element> fields = task.outputStaticNonFinalFieldLists[outputUnit];
542 // If the outputUnit does not contain any static non-final fields, then
543 // [fields] is `null`.
544 if (fields != null) {
545 for (Element element in fields) {
546 compiler.withCurrentElement(element, () {
547 ConstantValue constant = handler.getInitialValueFor(element);
548 parts.add(buildInitialization(element, constantReference(constant)));
549 });
550 }
551 }
552
553 if (inMainUnit && task.outputStaticNonFinalFieldLists.length > 1) {
554 // In the main output-unit we output a stub initializer for deferred
555 // variables, so that `isolateProperties` stays a fast object.
556 task.outputStaticNonFinalFieldLists.forEach(
557 (OutputUnit fieldsOutputUnit, Iterable<VariableElement> fields) {
558 if (fieldsOutputUnit == outputUnit) return; // Skip the main unit.
559 for (Element element in fields) {
560 compiler.withCurrentElement(element, () {
561 parts.add(buildInitialization(element, jsAst.number(0)));
562 });
563 }
564 });
565 }
566
567 return new jsAst.Block(parts);
568 }
569
570 jsAst.Statement buildLazilyInitializedStaticFields() {
571 JavaScriptConstantCompiler handler = backend.constants;
572 List<VariableElement> lazyFields =
573 handler.getLazilyInitializedFieldsForEmission();
574 if (lazyFields.isNotEmpty) {
575 needsLazyInitializer = true;
576 List<jsAst.Expression> laziesInfo = buildLaziesInfo(lazyFields);
577 return js.statement('''
578 (function(lazies) {
579 for (var i = 0; i < lazies.length; ) {
580 var fieldName = lazies[i++];
581 var getterName = lazies[i++];
582 if (#notMinified) {
583 var staticName = lazies[i++];
584 }
585 var lazyValue = lazies[i++];
586
587 // We build the lazy-check here:
588 // lazyInitializer(fieldName, getterName, lazyValue, staticName);
589 // 'staticName' is used for error reporting in non-minified mode.
590 // 'lazyValue' must be a closure that constructs the initial value.
591 if (#notMinified) {
592 #lazy(fieldName, getterName, lazyValue, staticName);
593 } else {
594 #lazy(fieldName, getterName, lazyValue);
595 }
596 }
597 })(#laziesInfo)
598 ''', {'notMinified': !compiler.enableMinification,
599 'laziesInfo': new jsAst.ArrayInitializer(laziesInfo),
600 'lazy': js(lazyInitializerName)});
601 } else {
602 return js.comment("No lazy statics.");
603 }
604 }
605
606 List<jsAst.Expression> buildLaziesInfo(List<VariableElement> lazies) {
607 List<jsAst.Expression> laziesInfo = <jsAst.Expression>[];
608 for (VariableElement element in Elements.sortedByPosition(lazies)) {
609 jsAst.Expression code = backend.generatedCode[element];
610 // The code is null if we ended up not needing the lazily
611 // initialized field after all because of constant folding
612 // before code generation.
613 if (code == null) continue;
614 laziesInfo.add(js.quoteName(namer.globalPropertyName(element)));
615 laziesInfo.add(js.quoteName(namer.lazyInitializerName(element)));
616 if (!compiler.enableMinification) {
617 laziesInfo.add(js.string(element.name));
618 }
619 laziesInfo.add(code);
620 }
621 return laziesInfo;
622 }
623
624 // TODO(sra): Remove this unused function.
625 jsAst.Expression buildLazilyInitializedStaticField(
626 VariableElement element, {String isolateProperties}) {
627 jsAst.Expression code = backend.generatedCode[element];
628 // The code is null if we ended up not needing the lazily
629 // initialized field after all because of constant folding
630 // before code generation.
631 if (code == null) return null;
632 // The code only computes the initial value. We build the lazy-check
633 // here:
634 // lazyInitializer(fieldName, getterName, initial, name, prototype);
635 // The name is used for error reporting. The 'initial' must be a
636 // closure that constructs the initial value.
637 if (isolateProperties != null) {
638 // This is currently only used in incremental compilation to patch
639 // in new lazy values.
640 return js('#(#,#,#,#,#)',
641 [js(lazyInitializerName),
642 js.quoteName(namer.globalPropertyName(element)),
643 js.quoteName(namer.lazyInitializerName(element)),
644 code,
645 js.string(element.name),
646 isolateProperties]);
647 }
648
649 if (compiler.enableMinification) {
650 return js('#(#,#,#)',
651 [js(lazyInitializerName),
652 js.quoteName(namer.globalPropertyName(element)),
653 js.quoteName(namer.lazyInitializerName(element)),
654 code]);
655 } else {
656 return js('#(#,#,#,#)',
657 [js(lazyInitializerName),
658 js.quoteName(namer.globalPropertyName(element)),
659 js.quoteName(namer.lazyInitializerName(element)),
660 code,
661 js.string(element.name)]);
662 }
663 }
664
665 jsAst.Statement buildMetadata(Program program, OutputUnit outputUnit) {
666 List<jsAst.Statement> parts = <jsAst.Statement>[];
667
668 jsAst.Expression types = program.metadataTypesForOutputUnit(outputUnit);
669
670 if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) {
671 jsAst.Expression metadataAccess =
672 generateEmbeddedGlobalAccess(embeddedNames.METADATA);
673 jsAst.Expression typesAccess =
674 generateEmbeddedGlobalAccess(embeddedNames.TYPES);
675
676 parts..add(js.statement('# = #;', [metadataAccess, program.metadata]))
677 ..add(js.statement('# = #;', [typesAccess, types]));
678 } else if (types != null) {
679 parts.add(js.statement('var ${namer.deferredTypesName} = #;',
680 types));
681 }
682 return new jsAst.Block(parts);
683 }
684
685 jsAst.Statement buildCompileTimeConstants(List<Constant> constants,
686 {bool isMainFragment}) {
687 assert(isMainFragment != null);
688
689 if (constants.isEmpty) return js.comment("No constants in program.");
690 List<jsAst.Statement> parts = <jsAst.Statement>[];
691 if (compiler.hasIncrementalSupport && isMainFragment) {
692 parts = cachedEmittedConstantsAst;
693 }
694 for (Constant constant in constants) {
695 ConstantValue constantValue = constant.value;
696 if (compiler.hasIncrementalSupport && isMainFragment) {
697 if (cachedEmittedConstants.contains(constantValue)) continue;
698 cachedEmittedConstants.add(constantValue);
699 }
700 parts.add(buildConstantInitializer(constantValue));
701 }
702
703 return new jsAst.Block(parts);
704 }
705
706 jsAst.Statement buildConstantInitializer(ConstantValue constant) {
707 jsAst.Name name = namer.constantName(constant);
708 return js.statement('#.# = #',
709 [namer.globalObjectForConstant(constant), name,
710 constantInitializerExpression(constant)]);
711 }
712
713 jsAst.Expression constantListGenerator(jsAst.Expression array) {
714 // TODO(floitsch): there is no harm in caching the template.
715 return js('${namer.isolateName}.#(#)', [makeConstListProperty, array]);
716 }
717
718 jsAst.Statement buildMakeConstantList() {
719 if (task.outputContainsConstantList) {
720 return js.statement(r'''
721 // Functions are stored in the hidden class and not as properties in
722 // the object. We never actually look at the value, but only want
723 // to know if the property exists.
724 #.# = function (list) {
725 list.immutable$list = Array;
726 list.fixed$length = Array;
727 return list;
728 }''',
729 [namer.isolateName, makeConstListProperty]);
730 } else {
731 return js.comment("Output contains no constant list.");
732 }
733 }
734
735 jsAst.Statement buildFunctionThatReturnsNull() {
736 return js.statement('#.# = function() {}',
737 [namer.isolateName,
738 backend.rti.getFunctionThatReturnsNullName]);
739 }
740
741 jsAst.Expression generateFunctionThatReturnsNull() {
742 return js("#.#", [namer.isolateName,
743 backend.rti.getFunctionThatReturnsNullName]);
744 }
745
746 buildMain(jsAst.Statement invokeMain) {
747 if (compiler.isMockCompilation) return js.comment("Mock compilation");
748
749 List<jsAst.Statement> parts = <jsAst.Statement>[];
750
751 if (NativeGenerator.needsIsolateAffinityTagInitialization(backend)) {
752 parts.add(
753 NativeGenerator.generateIsolateAffinityTagInitialization(
754 backend,
755 generateEmbeddedGlobalAccess,
756 js("convertToFastObject", [])));
757 }
758
759 parts..add(js.comment('BEGIN invoke [main].'))
760 ..add(invokeMain)
761 ..add(js.comment('END invoke [main].'));
762
763 return new jsAst.Block(parts);
764 }
765
766 jsAst.Statement buildInitFunction() {
767 jsAst.Expression allClassesAccess =
768 generateEmbeddedGlobalAccess(embeddedNames.ALL_CLASSES);
769 jsAst.Expression getTypeFromNameAccess =
770 generateEmbeddedGlobalAccess(embeddedNames.GET_TYPE_FROM_NAME);
771 jsAst.Expression interceptorsByTagAccess =
772 generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG);
773 jsAst.Expression leafTagsAccess =
774 generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS);
775 jsAst.Expression finishedClassesAccess =
776 generateEmbeddedGlobalAccess(embeddedNames.FINISHED_CLASSES);
777 jsAst.Expression cyclicThrow =
778 staticFunctionAccess(backend.getCyclicThrowHelper());
779 jsAst.Expression laziesAccess =
780 generateEmbeddedGlobalAccess(embeddedNames.LAZIES);
781
782 return js.statement('''
783 function init() {
784 $isolatePropertiesName = Object.create(null);
785 #allClasses = map();
786 #getTypeFromName = function(name) {return #allClasses[name];};
787 #interceptorsByTag = map();
788 #leafTags = map();
789 #finishedClasses = map();
790
791 if (#needsLazyInitializer) {
792 // [staticName] is only provided in non-minified mode. If missing, we
793 // fall back to [fieldName]. Likewise, [prototype] is optional and
794 // defaults to the isolateProperties object.
795 $lazyInitializerName = function (fieldName, getterName, lazyValue,
796 staticName, prototype) {
797 if (!#lazies) #lazies = Object.create(null);
798 #lazies[fieldName] = getterName;
799
800 // 'prototype' will be undefined except if we are doing an update
801 // during incremental compilation. In this case we put the lazy
802 // field directly on the isolate instead of the isolateProperties.
803 prototype = prototype || $isolatePropertiesName;
804 var sentinelUndefined = {};
805 var sentinelInProgress = {};
806 prototype[fieldName] = sentinelUndefined;
807
808 prototype[getterName] = function () {
809 var result = this[fieldName];
810 try {
811 if (result === sentinelUndefined) {
812 this[fieldName] = sentinelInProgress;
813
814 try {
815 result = this[fieldName] = lazyValue();
816 } finally {
817 // Use try-finally, not try-catch/throw as it destroys the
818 // stack trace.
819 if (result === sentinelUndefined)
820 this[fieldName] = null;
821 }
822 } else {
823 if (result === sentinelInProgress)
824 // In minified mode, static name is not provided, so fall
825 // back to the minified fieldName.
826 #cyclicThrow(staticName || fieldName);
827 }
828
829 return result;
830 } finally {
831 this[getterName] = function() { return this[fieldName]; };
832 }
833 }
834 }
835 }
836
837 // We replace the old Isolate function with a new one that initializes
838 // all its fields with the initial (and often final) value of all
839 // globals.
840 //
841 // We also copy over old values like the prototype, and the
842 // isolateProperties themselves.
843 $finishIsolateConstructorName = function (oldIsolate) {
844 var isolateProperties = oldIsolate.#isolatePropertiesName;
845 function Isolate() {
846
847 var staticNames = Object.keys(isolateProperties);
848 for (var i = 0; i < staticNames.length; i++) {
849 var staticName = staticNames[i];
850 this[staticName] = isolateProperties[staticName];
851 }
852
853 // Reset lazy initializers to null.
854 // When forcing the object to fast mode (below) v8 will consider
855 // functions as part the object's map. Since we will change them
856 // (after the first call to the getter), we would have a map
857 // transition.
858 var lazies = init.lazies;
859 var lazyInitializers = lazies ? Object.keys(lazies) : [];
860 for (var i = 0; i < lazyInitializers.length; i++) {
861 this[lazies[lazyInitializers[i]]] = null;
862 }
863
864 // Use the newly created object as prototype. In Chrome,
865 // this creates a hidden class for the object and makes
866 // sure it is fast to access.
867 function ForceEfficientMap() {}
868 ForceEfficientMap.prototype = this;
869 new ForceEfficientMap();
870
871 // Now, after being a fast map we can set the lazies again.
872 for (var i = 0; i < lazyInitializers.length; i++) {
873 var lazyInitName = lazies[lazyInitializers[i]];
874 this[lazyInitName] = isolateProperties[lazyInitName];
875 }
876 }
877 Isolate.prototype = oldIsolate.prototype;
878 Isolate.prototype.constructor = Isolate;
879 Isolate.#isolatePropertiesName = isolateProperties;
880 if (#outputContainsConstantList) {
881 Isolate.#makeConstListProperty = oldIsolate.#makeConstListProperty;
882 }
883 Isolate.#functionThatReturnsNullProperty =
884 oldIsolate.#functionThatReturnsNullProperty;
885 if (#hasIncrementalSupport) {
886 Isolate.#lazyInitializerProperty =
887 oldIsolate.#lazyInitializerProperty;
888 }
889 return Isolate;
890 }
891
892 }''', {'allClasses': allClassesAccess,
893 'getTypeFromName': getTypeFromNameAccess,
894 'interceptorsByTag': interceptorsByTagAccess,
895 'leafTags': leafTagsAccess,
896 'finishedClasses': finishedClassesAccess,
897 'needsLazyInitializer': needsLazyInitializer,
898 'lazies': laziesAccess, 'cyclicThrow': cyclicThrow,
899 'isolatePropertiesName': namer.isolatePropertiesName,
900 'outputContainsConstantList': task.outputContainsConstantList,
901 'makeConstListProperty': makeConstListProperty,
902 'functionThatReturnsNullProperty':
903 backend.rti.getFunctionThatReturnsNullName,
904 'hasIncrementalSupport': compiler.hasIncrementalSupport,
905 'lazyInitializerProperty': lazyInitializerProperty,});
906 }
907
908 jsAst.Statement buildConvertToFastObjectFunction() {
909 List<jsAst.Statement> debugCode = <jsAst.Statement>[];
910 if (DEBUG_FAST_OBJECTS) {
911 debugCode.add(js.statement(r'''
912 // The following only works on V8 when run with option
913 // "--allow-natives-syntax". We use'new Function' because the
914 // miniparser does not understand V8 native syntax.
915 if (typeof print === "function") {
916 var HasFastProperties =
917 new Function("a", "return %HasFastProperties(a)");
918 print("Size of global object: "
919 + String(Object.getOwnPropertyNames(properties).length)
920 + ", fast properties " + HasFastProperties(properties));
921 }'''));
922 }
923
924 return js.statement(r'''
925 function convertToFastObject(properties) {
926 // Create an instance that uses 'properties' as prototype. This should
927 // make 'properties' a fast object.
928 function MyClass() {};
929 MyClass.prototype = properties;
930 new MyClass();
931 #;
932 return properties;
933 }''', [debugCode]);
934 }
935
936 jsAst.Statement buildConvertToSlowObjectFunction() {
937 return js.statement(r'''
938 function convertToSlowObject(properties) {
939 // Add and remove a property to make the object transition into hashmap
940 // mode.
941 properties.__MAGIC_SLOW_PROPERTY = 1;
942 delete properties.__MAGIC_SLOW_PROPERTY;
943 return properties;
944 }''');
945 }
946
947 jsAst.Statement buildSupportsDirectProtoAccess() {
948 jsAst.Statement supportsDirectProtoAccess;
949
950 if (compiler.hasIncrementalSupport) {
951 supportsDirectProtoAccess = js.statement(r'''
952 var supportsDirectProtoAccess = false;
953 ''');
954 } else {
955 supportsDirectProtoAccess = js.statement(r'''
956 var supportsDirectProtoAccess = (function () {
957 var cls = function () {};
958 cls.prototype = {'p': {}};
959 var object = new cls();
960 return object.__proto__ &&
961 object.__proto__.p === cls.prototype.p;
962 })();
963 ''');
964 }
965
966 return supportsDirectProtoAccess;
967 }
968
969 jsAst.Expression generateLibraryDescriptor(LibraryElement library,
970 Fragment fragment) {
971 var uri = "";
972 if (!compiler.enableMinification || backend.mustPreserveUris) {
973 uri = library.canonicalUri;
974 if (uri.scheme == 'file' && compiler.outputUri != null) {
975 uri = relativize(compiler.outputUri, library.canonicalUri, false);
976 }
977 }
978
979 String libraryName =
980 (!compiler.enableMinification || backend.mustRetainLibraryNames) ?
981 library.getLibraryName() :
982 "";
983
984 jsAst.Fun metadata = task.metadataCollector.buildMetadataFunction(library);
985
986 ClassBuilder descriptor = elementDescriptors[fragment][library];
987
988 jsAst.ObjectInitializer initializer;
989 if (descriptor == null) {
990 // Nothing of the library was emitted.
991 // TODO(floitsch): this should not happen. We currently have an example
992 // with language/prefix6_negative_test.dart where we have an instance
993 // method without its corresponding class.
994 initializer = new jsAst.ObjectInitializer([]);
995 } else {
996 initializer = descriptor.toObjectInitializer();
997 }
998
999 compiler.dumpInfoTask.registerElementAst(library, metadata);
1000 compiler.dumpInfoTask.registerElementAst(library, initializer);
1001
1002 List<jsAst.Expression> parts = <jsAst.Expression>[];
1003 parts..add(js.string(libraryName))
1004 ..add(js.string(uri.toString()))
1005 ..add(metadata == null ? new jsAst.ArrayHole() : metadata)
1006 ..add(js('#', namer.globalObjectFor(library)))
1007 ..add(initializer);
1008 if (library == compiler.mainApp) {
1009 parts.add(js.number(1));
1010 }
1011
1012 return new jsAst.ArrayInitializer(parts);
1013 }
1014
1015 void assemblePrecompiledConstructor(OutputUnit outputUnit,
1016 jsAst.Name constructorName,
1017 jsAst.Expression constructorAst,
1018 List<jsAst.Name> fields) {
1019 cspPrecompiledFunctionFor(outputUnit).add(
1020 new jsAst.FunctionDeclaration(constructorName, constructorAst));
1021
1022 String fieldNamesProperty = FIELD_NAMES_PROPERTY_NAME;
1023 bool hasIsolateSupport = compiler.hasIsolateSupport;
1024 jsAst.Node fieldNamesArray;
1025 if (hasIsolateSupport) {
1026 fieldNamesArray =
1027 new jsAst.ArrayInitializer(fields.map(js.quoteName).toList());
1028 } else {
1029 fieldNamesArray = new jsAst.LiteralNull();
1030 }
1031
1032 cspPrecompiledFunctionFor(outputUnit).add(js.statement(r'''
1033 {
1034 #constructorName.#typeNameProperty = #constructorNameString;
1035 // IE does not have a name property.
1036 if (!("name" in #constructorName))
1037 #constructorName.name = #constructorNameString;
1038 $desc = $collectedClasses$.#constructorName[1];
1039 #constructorName.prototype = $desc;
1040 ''' /* next string is not a raw string */ '''
1041 if (#hasIsolateSupport) {
1042 #constructorName.$fieldNamesProperty = #fieldNamesArray;
1043 }
1044 }''',
1045 {"constructorName": constructorName,
1046 "typeNameProperty": typeNameProperty,
1047 "constructorNameString": js.quoteName(constructorName),
1048 "hasIsolateSupport": hasIsolateSupport,
1049 "fieldNamesArray": fieldNamesArray}));
1050
1051 cspPrecompiledConstructorNamesFor(outputUnit).add(js('#', constructorName));
1052 }
1053
1054 void assembleTypedefs(Program program) {
1055 Fragment mainFragment = program.mainFragment;
1056 OutputUnit mainOutputUnit = mainFragment.outputUnit;
1057
1058 // Emit all required typedef declarations into the main output unit.
1059 // TODO(karlklose): unify required classes and typedefs to declarations
1060 // and have builders for each kind.
1061 for (TypedefElement typedef in typedefsNeededForReflection) {
1062 LibraryElement library = typedef.library;
1063 // TODO(karlklose): add a TypedefBuilder and move this code there.
1064 DartType type = typedef.alias;
1065 // TODO(zarah): reify type variables once reflection on type arguments of
1066 // typedefs is supported.
1067 jsAst.Expression typeIndex =
1068 task.metadataCollector.reifyType(type, ignoreTypeVariables: true);
1069 ClassBuilder builder = new ClassBuilder.forStatics(typedef, namer);
1070 builder.addPropertyByName(embeddedNames.TYPEDEF_TYPE_PROPERTY_NAME,
1071 typeIndex);
1072 builder.addPropertyByName(embeddedNames.TYPEDEF_PREDICATE_PROPERTY_NAME,
1073 js.boolean(true));
1074
1075 // We can be pretty sure that the objectClass is initialized, since
1076 // typedefs are only emitted with reflection, which requires lots of
1077 // classes.
1078 assert(compiler.objectClass != null);
1079 builder.superName = namer.className(compiler.objectClass);
1080 jsAst.Node declaration = builder.toObjectInitializer();
1081 jsAst.Name mangledName = namer.globalPropertyName(typedef);
1082 String reflectionName = getReflectionName(typedef, mangledName);
1083 getElementDescriptor(library, mainFragment)
1084 ..addProperty(mangledName, declaration)
1085 ..addPropertyByName("+$reflectionName", js.string(''));
1086 // Also emit a trivial constructor for CSP mode.
1087 jsAst.Name constructorName = mangledName;
1088 jsAst.Expression constructorAst = js('function() {}');
1089 List<jsAst.Name> fieldNames = [];
1090 assemblePrecompiledConstructor(mainOutputUnit,
1091 constructorName,
1092 constructorAst,
1093 fieldNames);
1094 }
1095 }
1096
1097 jsAst.Statement buildGlobalObjectSetup(bool isProgramSplit) {
1098 List<jsAst.Statement> parts = <jsAst.Statement>[];
1099
1100 parts.add(js.comment("""
1101 // The global objects start as so-called "slow objects". For V8, this
1102 // means that it won't try to make map transitions as we add properties
1103 // to these objects. Later on, we attempt to turn these objects into
1104 // fast objects by calling "convertToFastObject" (see
1105 // [emitConvertToFastObjectFunction]).
1106 """));
1107
1108 for (String globalObject in Namer.reservedGlobalObjectNames) {
1109 if (isProgramSplit) {
1110 String template =
1111 "var #globalObject = #globalsHolder.#globalObject = map();";
1112 parts.add(js.statement(template, {"globalObject": globalObject,
1113 "globalsHolder": globalsHolder}));
1114 } else {
1115 parts.add(js.statement("var #globalObject = map();",
1116 {"globalObject": globalObject}));
1117 }
1118
1119 }
1120
1121 return new jsAst.Block(parts);
1122 }
1123
1124 jsAst.Statement buildConvertGlobalObjectToFastObjects() {
1125 List<jsAst.Statement> parts = <jsAst.Statement>[];
1126
1127 for (String globalObject in Namer.reservedGlobalObjectNames) {
1128 parts.add(js.statement(
1129 '#globalObject = convertToFastObject(#globalObject);',
1130 {"globalObject": globalObject}));
1131 }
1132
1133 return new jsAst.Block(parts);
1134 }
1135
1136 jsAst.Statement buildDebugFastObjectCode() {
1137 List<jsAst.Statement> parts = <jsAst.Statement>[];
1138
1139 if (DEBUG_FAST_OBJECTS) {
1140 parts.add(js.statement(r'''
1141 // The following only works on V8 when run with option
1142 // "--allow-natives-syntax". We use'new Function' because the
1143 // miniparser does not understand V8 native syntax.
1144 if (typeof print === "function") {
1145 var HasFastProperties =
1146 new Function("a", "return %HasFastProperties(a)");
1147 print("Size of global helper object: "
1148 + String(Object.getOwnPropertyNames(H).length)
1149 + ", fast properties " + HasFastProperties(H));
1150 print("Size of global platform object: "
1151 + String(Object.getOwnPropertyNames(P).length)
1152 + ", fast properties " + HasFastProperties(P));
1153 print("Size of global dart:html object: "
1154 + String(Object.getOwnPropertyNames(W).length)
1155 + ", fast properties " + HasFastProperties(W));
1156 print("Size of isolate properties object: "
1157 + String(Object.getOwnPropertyNames($).length)
1158 + ", fast properties " + HasFastProperties($));
1159 print("Size of constant object: "
1160 + String(Object.getOwnPropertyNames(C).length)
1161 + ", fast properties " + HasFastProperties(C));
1162 var names = Object.getOwnPropertyNames($);
1163 for (var i = 0; i < names.length; i++) {
1164 print("$." + names[i]);
1165 }
1166 }
1167 '''));
1168
1169 for (String object in Namer.userGlobalObjects) {
1170 parts.add(js.statement('''
1171 if (typeof print === "function") {
1172 print("Size of " + #objectString + ": "
1173 + String(Object.getOwnPropertyNames(#object).length)
1174 + ", fast properties " + HasFastProperties(#object));
1175 }
1176 ''', {"object": object, "objectString": js.string(object)}));
1177 }
1178 }
1179
1180 return new jsAst.Block(parts);
1181 }
1182
1183 jsAst.Statement buildMangledNames() {
1184 List<jsAst.Statement> parts = <jsAst.Statement>[];
1185
1186 if (!mangledFieldNames.isEmpty) {
1187 List<jsAst.Name> keys = mangledFieldNames.keys.toList()..sort();
1188 var properties = [];
1189 for (jsAst.Name key in keys) {
1190 var value = js.string(mangledFieldNames[key]);
1191 properties.add(new jsAst.Property(key, value));
1192 }
1193
1194 jsAst.Expression mangledNamesAccess =
1195 generateEmbeddedGlobalAccess(embeddedNames.MANGLED_NAMES);
1196 var map = new jsAst.ObjectInitializer(properties);
1197 parts.add(js.statement('# = #', [mangledNamesAccess, map]));
1198 }
1199
1200 if (!mangledGlobalFieldNames.isEmpty) {
1201 List<jsAst.Name> keys = mangledGlobalFieldNames.keys.toList()
1202 ..sort();
1203 List<jsAst.Property> properties = <jsAst.Property>[];
1204 for (jsAst.Name key in keys) {
1205 jsAst.Literal value = js.string(mangledGlobalFieldNames[key]);
1206 properties.add(new jsAst.Property(js.quoteName(key), value));
1207 }
1208 jsAst.Expression mangledGlobalNamesAccess =
1209 generateEmbeddedGlobalAccess(embeddedNames.MANGLED_GLOBAL_NAMES);
1210 jsAst.ObjectInitializer map = new jsAst.ObjectInitializer(properties);
1211 parts.add(js.statement('# = #', [mangledGlobalNamesAccess, map]));
1212 }
1213
1214 return new jsAst.Block(parts);
1215 }
1216
1217 void checkEverythingEmitted(Iterable<Element> elements) {
1218 List<Element> pendingStatics;
1219 if (!compiler.hasIncrementalSupport) {
1220 pendingStatics =
1221 Elements.sortedByPosition(elements.where((e) => !e.isLibrary));
1222
1223 pendingStatics.forEach((element) =>
1224 compiler.reportInfo(
1225 element, MessageKind.GENERIC, {'text': 'Pending statics.'}));
1226 }
1227
1228 if (pendingStatics != null && !pendingStatics.isEmpty) {
1229 compiler.internalError(pendingStatics.first,
1230 'Pending statics (see above).');
1231 }
1232 }
1233
1234 void assembleLibrary(Library library, Fragment fragment) {
1235 LibraryElement libraryElement = library.element;
1236
1237 assembleStaticFunctions(library.statics, fragment);
1238
1239 ClassBuilder libraryBuilder =
1240 getElementDescriptor(libraryElement, fragment);
1241 for (Class cls in library.classes) {
1242 assembleClass(cls, libraryBuilder, fragment);
1243 }
1244
1245 classEmitter.emitFields(library, libraryBuilder, emitStatics: true);
1246 }
1247
1248 void assembleProgram(Program program) {
1249 for (Fragment fragment in program.fragments) {
1250 for (Library library in fragment.libraries) {
1251 assembleLibrary(library, fragment);
1252 }
1253 }
1254 assembleTypedefs(program);
1255 }
1256
1257 jsAst.Program buildOutputAstForMain(Program program,
1258 Map<OutputUnit, _DeferredOutputUnitHash> deferredLoadHashes) {
1259 MainFragment mainFragment = program.mainFragment;
1260 OutputUnit mainOutputUnit = mainFragment.outputUnit;
1261 bool isProgramSplit = program.isSplit;
1262
1263 List<jsAst.Statement> statements = <jsAst.Statement>[];
1264
1265 statements..add(buildGeneratedBy())
1266 ..add(js.comment(HOOKS_API_USAGE));
1267
1268 if (isProgramSplit) {
1269 /// For deferred loading we communicate the initializers via this global
1270 /// variable. The deferred hunks will add their initialization to this.
1271 /// The semicolon is important in minified mode, without it the
1272 /// following parenthesis looks like a call to the object literal.
1273 statements.add(
1274 js.statement('self.#deferredInitializers = '
1275 'self.#deferredInitializers || Object.create(null);',
1276 {'deferredInitializers': deferredInitializers}));
1277 }
1278
1279 // Collect the AST for the decriptors
1280 Map<Element, ClassBuilder> descriptors = elementDescriptors[mainFragment];
1281 if (descriptors == null) descriptors = const {};
1282
1283 checkEverythingEmitted(descriptors.keys);
1284
1285 Iterable<LibraryElement> libraries =
1286 task.outputLibraryLists[mainOutputUnit];
1287 if (libraries == null) libraries = <LibraryElement>[];
1288
1289 List<jsAst.Expression> parts = <jsAst.Expression>[];
1290 for (LibraryElement library in Elements.sortedByPosition(libraries)) {
1291 parts.add(generateLibraryDescriptor(library, mainFragment));
1292 descriptors.remove(library);
1293 }
1294
1295 if (descriptors.isNotEmpty) {
1296 List<Element> remainingLibraries = descriptors.keys
1297 .where((Element e) => e is LibraryElement)
1298 .toList();
1299
1300 // The remaining descriptors are only accessible through reflection.
1301 // The program builder does not collect libraries that only
1302 // contain typedefs that are used for reflection.
1303 for (LibraryElement element in remainingLibraries) {
1304 assert(element is LibraryElement || compiler.hasIncrementalSupport);
1305 if (element is LibraryElement) {
1306 parts.add(generateLibraryDescriptor(element, mainFragment));
1307 descriptors.remove(element);
1308 }
1309 }
1310 }
1311 jsAst.ArrayInitializer descriptorsAst = new jsAst.ArrayInitializer(parts);
1312
1313 // Using a named function here produces easier to read stack traces in
1314 // Chrome/V8.
1315 statements.add(js.statement("""
1316 (function() {
1317 // No renaming in the top-level function to save the locals for the
1318 // nested context where they will be used more. We have to put the
1319 // comment into a hole as the parser strips out comments right away.
1320 #disableVariableRenaming;
1321 #supportsDirectProtoAccess;
1322
1323 if (#hasIncrementalSupport) {
1324 #helper = #helper || Object.create(null);
1325 #helper.patch = function(a) { eval(a)};
1326 #helper.schemaChange = #schemaChange;
1327 #helper.addMethod = #addMethod;
1328 #helper.extractStubs =
1329 function(array, name, isStatic, originalDescriptor) {
1330 var descriptor = Object.create(null);
1331 this.addStubs(descriptor, array, name, isStatic, []);
1332 return descriptor;
1333 };
1334 }
1335
1336 if (#isProgramSplit) {
1337 /// We collect all the global state, so it can be passed to the
1338 /// initializer of deferred files.
1339 var #globalsHolder = Object.create(null)
1340 }
1341
1342 // [map] returns an object that V8 shouldn't try to optimize with a
1343 // hidden class. This prevents a potential performance problem where V8
1344 // tries to build a hidden class for an object used as a hashMap.
1345 // It requires fewer characters to declare a variable as a parameter than
1346 // with `var`.
1347 function map(x) {
1348 x = Object.create(null);
1349 x.x = 0;
1350 delete x.x;
1351 return x;
1352 }
1353
1354 #globalObjectSetup;
1355
1356 function #isolateName() {}
1357
1358 if (#isProgramSplit) {
1359 #globalsHolder.#isolateName = #isolateName;
1360 #globalsHolder.#initName = #initName;
1361 #globalsHolder.#setupProgramName = #setupProgramName;
1362 }
1363
1364 init();
1365
1366 #mangledNames;
1367
1368 #cspPrecompiledFunctions;
1369
1370 #setupProgram;
1371
1372 #functionThatReturnsNull;
1373
1374 // The argument to reflectionDataParser is assigned to a temporary 'dart'
1375 // so that 'dart.' will appear as the prefix to dart methods in stack
1376 // traces and profile entries.
1377 var dart = #descriptors;
1378
1379 #setupProgramName(dart, 0);
1380
1381 #getInterceptorMethods;
1382 #oneShotInterceptors;
1383
1384 #makeConstantList;
1385
1386 // We abuse the short name used for the isolate here to store
1387 // the isolate properties. This is safe as long as the real isolate
1388 // object does not exist yet.
1389 var ${namer.currentIsolate} = #isolatePropertiesName;
1390
1391 // Constants in checked mode call into RTI code to set type information
1392 // which may need getInterceptor (and one-shot interceptor) methods, so
1393 // we have to make sure that [emitGetInterceptorMethods] and
1394 // [emitOneShotInterceptors] have been called.
1395 #compileTimeConstants;
1396
1397 // Static field initializations require the classes and compile-time
1398 // constants to be set up.
1399 #staticNonFinalInitializers;
1400
1401 ${namer.currentIsolate} = null;
1402
1403 #deferredBoilerPlate;
1404
1405 #typeToInterceptorMap;
1406
1407 #lazyStaticFields;
1408
1409 #isolateName = $finishIsolateConstructorName(#isolateName);
1410
1411 ${namer.currentIsolate} = new #isolateName();
1412
1413 #metadata;
1414
1415 #convertToFastObject;
1416 #convertToSlowObject;
1417
1418 #convertGlobalObjectsToFastObjects;
1419 #debugFastObjects;
1420
1421 #init;
1422
1423 #main;
1424 })();
1425 """, {
1426 "disableVariableRenaming": js.comment("/* ::norenaming:: */"),
1427 "hasIncrementalSupport": compiler.hasIncrementalSupport,
1428 "helper": js('this.#', [namer.incrementalHelperName]),
1429 "schemaChange": buildSchemaChangeFunction(),
1430 "addMethod": buildIncrementalAddMethod(),
1431 "isProgramSplit": isProgramSplit,
1432 "supportsDirectProtoAccess": buildSupportsDirectProtoAccess(),
1433 "globalsHolder": globalsHolder,
1434 "globalObjectSetup": buildGlobalObjectSetup(isProgramSplit),
1435 "isolateName": namer.isolateName,
1436 "isolatePropertiesName": js(isolatePropertiesName),
1437 "initName": initName,
1438 "functionThatReturnsNull": buildFunctionThatReturnsNull(),
1439 "mangledNames": buildMangledNames(),
1440 "setupProgram": buildSetupProgram(program, compiler, backend, namer, this) ,
1441 "setupProgramName": setupProgramName,
1442 "descriptors": descriptorsAst,
1443 "cspPrecompiledFunctions": buildCspPrecompiledFunctionFor(mainOutputUnit),
1444 "getInterceptorMethods": interceptorEmitter.buildGetInterceptorMethods(),
1445 "oneShotInterceptors": interceptorEmitter.buildOneShotInterceptors(),
1446 "makeConstantList": buildMakeConstantList(),
1447 "compileTimeConstants": buildCompileTimeConstants(mainFragment.constants,
1448 isMainFragment: true),
1449 "deferredBoilerPlate": buildDeferredBoilerPlate(deferredLoadHashes),
1450 "staticNonFinalInitializers": buildStaticNonFinalFieldInitializations(
1451 mainOutputUnit),
1452 "typeToInterceptorMap":
1453 interceptorEmitter.buildTypeToInterceptorMap(program),
1454 "lazyStaticFields": buildLazilyInitializedStaticFields(),
1455 "metadata": buildMetadata(program, mainOutputUnit),
1456 "convertToFastObject": buildConvertToFastObjectFunction(),
1457 "convertToSlowObject": buildConvertToSlowObjectFunction(),
1458 "convertGlobalObjectsToFastObjects":
1459 buildConvertGlobalObjectToFastObjects(),
1460 "debugFastObjects": buildDebugFastObjectCode(),
1461 "init": buildInitFunction(),
1462 "main": buildMain(mainFragment.invokeMain)
1463 }));
1464
1465 return new jsAst.Program(statements);
1466 }
1467
1468 void emitMainOutputUnit(OutputUnit mainOutputUnit, jsAst.Program program) {
1469 LineColumnCollector lineColumnCollector;
1470 List<CodeOutputListener> codeOutputListeners;
1471 if (generateSourceMap) {
1472 lineColumnCollector = new LineColumnCollector();
1473 codeOutputListeners = <CodeOutputListener>[lineColumnCollector];
1474 }
1475
1476 CodeOutput mainOutput =
1477 new StreamCodeOutput(compiler.outputProvider('', 'js'),
1478 codeOutputListeners);
1479 outputBuffers[mainOutputUnit] = mainOutput;
1480
1481
1482 mainOutput.addBuffer(jsAst.prettyPrint(program,
1483 compiler,
1484 monitor: compiler.dumpInfoTask));
1485
1486 if (compiler.deferredMapUri != null) {
1487 outputDeferredMap();
1488 }
1489
1490 if (generateSourceMap) {
1491 mainOutput.add(
1492 generateSourceMapTag(compiler.sourceMapUri, compiler.outputUri));
1493 }
1494
1495 mainOutput.close();
1496
1497 if (generateSourceMap) {
1498 outputSourceMap(mainOutput, lineColumnCollector, '',
1499 compiler.sourceMapUri, compiler.outputUri);
1500 }
1501 }
1502
1503 /// Used by incremental compilation to patch up the prototype of
1504 /// [oldConstructor] for use as prototype of [newConstructor].
1505 jsAst.Fun buildSchemaChangeFunction() {
1506 if (!compiler.hasIncrementalSupport) return null;
1507 return js('''
1508 function(newConstructor, oldConstructor, superclass) {
1509 // Invariant: newConstructor.prototype has no interesting properties besides
1510 // generated accessors. These are copied to oldPrototype which will be
1511 // updated by other incremental changes.
1512 if (superclass != null) {
1513 this.inheritFrom(newConstructor, superclass);
1514 }
1515 var oldPrototype = oldConstructor.prototype;
1516 var newPrototype = newConstructor.prototype;
1517 var hasOwnProperty = Object.prototype.hasOwnProperty;
1518 for (var property in newPrototype) {
1519 if (hasOwnProperty.call(newPrototype, property)) {
1520 // Copy generated accessors.
1521 oldPrototype[property] = newPrototype[property];
1522 }
1523 }
1524 oldPrototype.__proto__ = newConstructor.prototype.__proto__;
1525 oldPrototype.constructor = newConstructor;
1526 newConstructor.prototype = oldPrototype;
1527 return newConstructor;
1528 }''');
1529 }
1530
1531 /// Used by incremental compilation to patch up an object ([holder]) with a
1532 /// new (or updated) method. [arrayOrFunction] is either the new method, or
1533 /// an array containing the method (see
1534 /// [ContainerBuilder.addMemberMethodFromInfo]). [name] is the name of the
1535 /// new method. [isStatic] tells if method is static (or
1536 /// top-level). [globalFunctionsAccess] is a reference to
1537 /// [embeddedNames.GLOBAL_FUNCTIONS].
1538 jsAst.Fun buildIncrementalAddMethod() {
1539 if (!compiler.hasIncrementalSupport) return null;
1540 return js(r"""
1541 function(originalDescriptor, name, holder, isStatic, globalFunctionsAccess) {
1542 var arrayOrFunction = originalDescriptor[name];
1543 var method;
1544 if (arrayOrFunction.constructor === Array) {
1545 var existing = holder[name];
1546 var array = arrayOrFunction;
1547
1548 // Each method may have a number of stubs associated. For example, if an
1549 // instance method supports multiple arguments, a stub for each matching
1550 // selector. There is also a getter stub for tear-off getters. For example,
1551 // an instance method foo([a]) may have the following stubs: foo$0, foo$1,
1552 // and get$foo (here exemplified using unminified names).
1553 // [extractStubs] returns a JavaScript object whose own properties
1554 // corresponds to the stubs.
1555 var descriptor =
1556 this.extractStubs(array, name, isStatic, originalDescriptor);
1557 method = descriptor[name];
1558
1559 // Iterate through the properties of descriptor and copy the stubs to the
1560 // existing holder (for instance methods, a prototype).
1561 for (var property in descriptor) {
1562 if (!Object.prototype.hasOwnProperty.call(descriptor, property)) continue;
1563 var stub = descriptor[property];
1564 var existingStub = holder[property];
1565 if (stub === method || !existingStub || !stub.$getterStub) {
1566 // Not replacing an existing getter stub.
1567 holder[property] = stub;
1568 continue;
1569 }
1570 if (!stub.$getterStub) {
1571 var error = new Error('Unexpected stub.');
1572 error.stub = stub;
1573 throw error;
1574 }
1575
1576 // Existing getter stubs need special treatment as they may already have
1577 // been called and produced a closure.
1578 this.pendingStubs = this.pendingStubs || [];
1579 // It isn't safe to invoke the stub yet.
1580 this.pendingStubs.push((function(holder, stub, existingStub, existing,
1581 method) {
1582 return function() {
1583 var receiver = isStatic ? holder : new holder.constructor();
1584 // Invoke the existing stub to obtain the tear-off closure.
1585 existingStub = existingStub.call(receiver);
1586 // Invoke the new stub to create a tear-off closure we can use as a
1587 // prototype.
1588 stub = stub.call(receiver);
1589
1590 // Copy the properties from the new tear-off's prototype to the
1591 // prototype of the existing tear-off.
1592 var newProto = stub.constructor.prototype;
1593 var existingProto = existingStub.constructor.prototype;
1594 for (var stubProperty in newProto) {
1595 if (!Object.prototype.hasOwnProperty.call(newProto, stubProperty))
1596 continue;
1597 existingProto[stubProperty] = newProto[stubProperty];
1598 }
1599
1600 // Update all the existing stub's references to [existing] to
1601 // [method]. Instance tear-offs are call-by-name, so this isn't
1602 // necessary for those.
1603 if (!isStatic) return;
1604 for (var reference in existingStub) {
1605 if (existingStub[reference] === existing) {
1606 existingStub[reference] = method;
1607 }
1608 }
1609 }
1610 })(holder, stub, existingStub, existing, method));
1611 }
1612 } else {
1613 method = arrayOrFunction;
1614 holder[name] = method;
1615 }
1616 if (isStatic) globalFunctionsAccess[name] = method;
1617 }""");
1618 }
1619
1620 Map<OutputUnit, jsAst.Expression> buildDescriptorsForOutputUnits(
1621 Program program) {
1622 Map<OutputUnit, jsAst.Expression> outputs =
1623 new Map<OutputUnit, jsAst.Expression>();
1624
1625 for (Fragment fragment in program.deferredFragments) {
1626 OutputUnit outputUnit = fragment.outputUnit;
1627
1628 Map<Element, ClassBuilder> descriptors = elementDescriptors[fragment];
1629
1630 if (descriptors != null && descriptors.isNotEmpty) {
1631 Iterable<LibraryElement> libraries =
1632 task.outputLibraryLists[outputUnit];
1633 if (libraries == null) libraries = [];
1634
1635 // TODO(johnniwinther): Avoid creating [CodeBuffer]s.
1636 List<jsAst.Expression> parts = <jsAst.Expression>[];
1637 for (LibraryElement library in Elements.sortedByPosition(libraries)) {
1638 parts.add(generateLibraryDescriptor(library, fragment));
1639 descriptors.remove(library);
1640 }
1641
1642 outputs[outputUnit] = new jsAst.ArrayInitializer(parts);
1643 }
1644 }
1645
1646 return outputs;
1647 }
1648
1649 void finalizeTokensInAst(jsAst.Program main,
1650 Iterable<jsAst.Program> deferredParts) {
1651 jsAst.TokenCounter counter = new jsAst.TokenCounter();
1652 counter.countTokens(main);
1653 deferredParts.forEach(counter.countTokens);
1654 task.metadataCollector.finalizeTokens();
1655 if (backend.namer is jsAst.TokenFinalizer) {
1656 var finalizer = backend.namer;
1657 finalizer.finalizeTokens();
1658 }
1659 }
1660
1661 int emitProgram(ProgramBuilder programBuilder) {
1662 Program program = programBuilder.buildProgram(
1663 storeFunctionTypesInMetadata: true);
1664
1665 assembleProgram(program);
1666
1667 // Construct the ASTs for all deferred output units.
1668 Map<OutputUnit, jsAst.Program> deferredParts =
1669 buildOutputAstForDeferredCode(program);
1670
1671 Map<OutputUnit, _DeferredOutputUnitHash> deferredHashTokens =
1672 new Map<OutputUnit, _DeferredOutputUnitHash>.fromIterables(
1673 deferredParts.keys,
1674 deferredParts.keys.map((OutputUnit unit) {
1675 return new _DeferredOutputUnitHash(unit);
1676 })
1677 );
1678
1679 jsAst.Program mainOutput =
1680 buildOutputAstForMain(program, deferredHashTokens);
1681
1682 finalizeTokensInAst(mainOutput, deferredParts.values);
1683
1684 // Emit deferred units first, so we have their hashes.
1685 // Map from OutputUnit to a hash of its content. The hash uniquely
1686 // identifies the code of the output-unit. It does not include
1687 // boilerplate JS code, like the sourcemap directives or the hash
1688 // itself.
1689 Map<OutputUnit, String> deferredLoadHashes =
1690 emitDeferredOutputUnits(deferredParts);
1691
1692 deferredHashTokens.forEach((OutputUnit key, _DeferredOutputUnitHash token) {
1693 token.setHash(deferredLoadHashes[key]);
1694 });
1695 emitMainOutputUnit(program.mainFragment.outputUnit, mainOutput);
1696
1697 if (backend.requiresPreamble &&
1698 !backend.htmlLibraryIsLoaded) {
1699 compiler.reportHint(NO_LOCATION_SPANNABLE, MessageKind.PREAMBLE);
1700 }
1701 // Return the total program size.
1702 return outputBuffers.values.fold(0, (a, b) => a + b.length);
1703 }
1704
1705 String generateSourceMapTag(Uri sourceMapUri, Uri fileUri) {
1706 if (sourceMapUri != null && fileUri != null) {
1707 String sourceMapFileName = relativize(fileUri, sourceMapUri, false);
1708 return '''
1709
1710 //# sourceMappingURL=$sourceMapFileName
1711 ''';
1712 }
1713 return '';
1714 }
1715
1716 ClassBuilder getElementDescriptor(Element element, Fragment fragment) {
1717 Element owner = element.library;
1718 bool isClass = false;
1719 if (!element.isLibrary && !element.isTopLevel && !element.isNative) {
1720 // For static (not top level) elements, record their code in a buffer
1721 // specific to the class. For now, not supported for native classes and
1722 // native elements.
1723 ClassElement cls =
1724 element.enclosingClassOrCompilationUnit.declaration;
1725 if (compiler.codegenWorld.directlyInstantiatedClasses.contains(cls) &&
1726 !cls.isNative &&
1727 compiler.deferredLoadTask.outputUnitForElement(element) ==
1728 compiler.deferredLoadTask.outputUnitForElement(cls)) {
1729 owner = cls;
1730 }
1731 }
1732 if (owner == null) {
1733 compiler.internalError(element, 'Owner is null.');
1734 }
1735 return elementDescriptors
1736 .putIfAbsent(fragment, () => new Map<Element, ClassBuilder>())
1737 .putIfAbsent(owner, () {
1738 return new ClassBuilder(owner, namer, owner.isClass);
1739 });
1740 }
1741
1742 /// Emits support-code for deferred loading into [output].
1743 jsAst.Statement buildDeferredBoilerPlate(
1744 Map<OutputUnit, _DeferredOutputUnitHash> deferredLoadHashes) {
1745 List<jsAst.Statement> parts = <jsAst.Statement>[];
1746
1747 parts.add(js.statement('''
1748 {
1749 // Function for checking if a hunk is loaded given its hash.
1750 #isHunkLoaded = function(hunkHash) {
1751 return !!$deferredInitializers[hunkHash];
1752 };
1753 #deferredInitialized = new Object(null);
1754 // Function for checking if a hunk is initialized given its hash.
1755 #isHunkInitialized = function(hunkHash) {
1756 return #deferredInitialized[hunkHash];
1757 };
1758 // Function for initializing a loaded hunk, given its hash.
1759 #initializeLoadedHunk = function(hunkHash) {
1760 $deferredInitializers[hunkHash](
1761 #globalsHolder, ${namer.currentIsolate});
1762 #deferredInitialized[hunkHash] = true;
1763 };
1764 }
1765 ''', {"globalsHolder": globalsHolder,
1766 "isHunkLoaded": generateEmbeddedGlobalAccess(
1767 embeddedNames.IS_HUNK_LOADED),
1768 "isHunkInitialized": generateEmbeddedGlobalAccess(
1769 embeddedNames.IS_HUNK_INITIALIZED),
1770 "initializeLoadedHunk": generateEmbeddedGlobalAccess(
1771 embeddedNames.INITIALIZE_LOADED_HUNK),
1772 "deferredInitialized": generateEmbeddedGlobalAccess(
1773 embeddedNames.DEFERRED_INITIALIZED)}));
1774
1775 // Write a javascript mapping from Deferred import load ids (derrived
1776 // from the import prefix.) to a list of lists of uris of hunks to load,
1777 // and a corresponding mapping to a list of hashes used by
1778 // INITIALIZE_LOADED_HUNK and IS_HUNK_LOADED.
1779 Map<String, List<jsAst.LiteralString>> deferredLibraryUris =
1780 new Map<String, List<jsAst.LiteralString>>();
1781 Map<String, List<_DeferredOutputUnitHash>> deferredLibraryHashes =
1782 new Map<String, List<_DeferredOutputUnitHash>>();
1783 compiler.deferredLoadTask.hunksToLoad.forEach(
1784 (String loadId, List<OutputUnit>outputUnits) {
1785 List<jsAst.LiteralString> uris = new List<jsAst.LiteralString>();
1786 List<_DeferredOutputUnitHash> hashes =
1787 new List<_DeferredOutputUnitHash>();
1788 deferredLibraryHashes[loadId] = new List<_DeferredOutputUnitHash>();
1789 for (OutputUnit outputUnit in outputUnits) {
1790 uris.add(js.escapedString(
1791 backend.deferredPartFileName(outputUnit.name)));
1792 hashes.add(deferredLoadHashes[outputUnit]);
1793 }
1794
1795 deferredLibraryUris[loadId] = uris;
1796 deferredLibraryHashes[loadId] = hashes;
1797 });
1798
1799 void emitMapping(String name, Map<String, List<jsAst.Expression>> mapping) {
1800 List<jsAst.Property> properties = new List<jsAst.Property>();
1801 mapping.forEach((String key, List<jsAst.Expression> values) {
1802 properties.add(new jsAst.Property(js.escapedString(key),
1803 new jsAst.ArrayInitializer(values)));
1804 });
1805 jsAst.Node initializer =
1806 new jsAst.ObjectInitializer(properties, isOneLiner: true);
1807
1808 jsAst.Node globalName = generateEmbeddedGlobalAccess(name);
1809 parts.add(js.statement("# = #", [globalName, initializer]));
1810 }
1811
1812 emitMapping(embeddedNames.DEFERRED_LIBRARY_URIS, deferredLibraryUris);
1813 emitMapping(embeddedNames.DEFERRED_LIBRARY_HASHES,
1814 deferredLibraryHashes);
1815
1816 return new jsAst.Block(parts);
1817 }
1818
1819 Map <OutputUnit, jsAst.Program> buildOutputAstForDeferredCode(
1820 Program program) {
1821 if (!program.isSplit) return const <OutputUnit, jsAst.Program>{};
1822
1823 Map<OutputUnit, jsAst.Program> result =
1824 new Map<OutputUnit, jsAst.Program>();
1825
1826 Map<OutputUnit, jsAst.Expression> deferredAsts =
1827 buildDescriptorsForOutputUnits(program);
1828
1829 for (Fragment fragment in program.deferredFragments) {
1830 OutputUnit outputUnit = fragment.outputUnit;
1831 jsAst.Expression libraryDescriptor = deferredAsts[outputUnit];
1832 List<jsAst.Statement> body = <jsAst.Statement>[];
1833
1834 // No renaming in the top-level function to save the locals for the
1835 // nested context where they will be used more.
1836 body.add(js.comment("/* ::norenaming:: "));
1837
1838 for (String globalObject in Namer.reservedGlobalObjectNames) {
1839 body.add(js.statement('var #object = #globalsHolder.#object;',
1840 {'globalsHolder': globalsHolder,
1841 'object': globalObject}));
1842 }
1843 body..add(js.statement('var init = #globalsHolder.init;',
1844 {'globalsHolder': globalsHolder}))
1845 ..add(js.statement('var $setupProgramName = '
1846 '#globalsHolder.$setupProgramName;',
1847 {'globalsHolder': globalsHolder}))
1848 ..add(js.statement('var ${namer.isolateName} = '
1849 '#globalsHolder.${namer.isolateName};',
1850 {'globalsHolder': globalsHolder}));
1851 String typesAccess =
1852 generateEmbeddedGlobalAccessString(embeddedNames.TYPES);
1853 if (libraryDescriptor != null) {
1854 // The argument to reflectionDataParser is assigned to a temporary
1855 // 'dart' so that 'dart.' will appear as the prefix to dart methods
1856 // in stack traces and profile entries.
1857 body.add(js.statement('var dart = #', libraryDescriptor));
1858
1859 if (compiler.useContentSecurityPolicy) {
1860 body.add(buildCspPrecompiledFunctionFor(outputUnit));
1861 }
1862 body.add(
1863 js.statement('$setupProgramName(dart, ${typesAccess}.length);'));
1864 }
1865
1866 body..add(buildMetadata(program, outputUnit))
1867 ..add(js.statement('${typesAccess}.push.apply(${typesAccess}, '
1868 '${namer.deferredTypesName});'));
1869
1870 // Set the currentIsolate variable to the current isolate (which is
1871 // provided as second argument).
1872 body.add(js.statement("${namer.currentIsolate} = arguments[1];"));
1873
1874 body.add(buildCompileTimeConstants(fragment.constants,
1875 isMainFragment: false));
1876 body.add(buildStaticNonFinalFieldInitializations(outputUnit));
1877
1878 List<jsAst.Statement> statements = <jsAst.Statement>[];
1879
1880 statements
1881 ..add(buildGeneratedBy())
1882 ..add(js.statement('${deferredInitializers}.current = '
1883 """function (#) {
1884 #
1885 }
1886 """, [globalsHolder, body]));
1887
1888 result[outputUnit] = new jsAst.Program(statements);
1889 }
1890
1891 return result;
1892 }
1893
1894 /// Returns a map from OutputUnit to a hash of its content. The hash uniquely
1895 /// identifies the code of the output-unit. It does not include
1896 /// boilerplate JS code, like the sourcemap directives or the hash
1897 /// itself.
1898 Map<OutputUnit, String> emitDeferredOutputUnits(
1899 Map<OutputUnit, jsAst.Program> outputAsts) {
1900
1901 Map<OutputUnit, String> hunkHashes = new Map<OutputUnit, String>();
1902
1903 for (OutputUnit outputUnit in outputAsts.keys) {
1904 List<CodeOutputListener> outputListeners = <CodeOutputListener>[];
1905 Hasher hasher = new Hasher();
1906 outputListeners.add(hasher);
1907
1908 LineColumnCollector lineColumnCollector;
1909 if (generateSourceMap) {
1910 lineColumnCollector = new LineColumnCollector();
1911 outputListeners.add(lineColumnCollector);
1912 }
1913
1914 String partPrefix =
1915 backend.deferredPartFileName(outputUnit.name, addExtension: false);
1916 CodeOutput output = new StreamCodeOutput(
1917 compiler.outputProvider(partPrefix, 'part.js'),
1918 outputListeners);
1919
1920 outputBuffers[outputUnit] = output;
1921
1922 output.addBuffer(jsAst.prettyPrint(outputAsts[outputUnit],
1923 compiler,
1924 monitor: compiler.dumpInfoTask));
1925
1926 // Make a unique hash of the code (before the sourcemaps are added)
1927 // This will be used to retrieve the initializing function from the global
1928 // variable.
1929 String hash = hasher.getHash();
1930
1931 output.add('$N${deferredInitializers}["$hash"]$_=$_'
1932 '${deferredInitializers}.current$N');
1933
1934 if (generateSourceMap) {
1935 Uri mapUri, partUri;
1936 Uri sourceMapUri = compiler.sourceMapUri;
1937 Uri outputUri = compiler.outputUri;
1938
1939 String partName = "$partPrefix.part";
1940
1941 if (sourceMapUri != null) {
1942 String mapFileName = partName + ".js.map";
1943 List<String> mapSegments = sourceMapUri.pathSegments.toList();
1944 mapSegments[mapSegments.length - 1] = mapFileName;
1945 mapUri = compiler.sourceMapUri.replace(pathSegments: mapSegments);
1946 }
1947
1948 if (outputUri != null) {
1949 String partFileName = partName + ".js";
1950 List<String> partSegments = outputUri.pathSegments.toList();
1951 partSegments[partSegments.length - 1] = partFileName;
1952 partUri = compiler.outputUri.replace(pathSegments: partSegments);
1953 }
1954
1955 output.add(generateSourceMapTag(mapUri, partUri));
1956 output.close();
1957 outputSourceMap(output, lineColumnCollector, partName,
1958 mapUri, partUri);
1959 } else {
1960 output.close();
1961 }
1962
1963 hunkHashes[outputUnit] = hash;
1964 }
1965 return hunkHashes;
1966 }
1967
1968 jsAst.Comment buildGeneratedBy() {
1969 String suffix = '';
1970 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
1971 String msg = '// Generated by dart2js, the Dart to JavaScript '
1972 'compiler$suffix.';
1973 return new jsAst.Comment(msg);
1974 }
1975
1976 void outputSourceMap(CodeOutput output,
1977 LineColumnProvider lineColumnProvider,
1978 String name,
1979 [Uri sourceMapUri,
1980 Uri fileUri]) {
1981 if (!generateSourceMap) return;
1982 // Create a source file for the compilation output. This allows using
1983 // [:getLine:] to transform offsets to line numbers in [SourceMapBuilder].
1984 SourceMapBuilder sourceMapBuilder =
1985 new SourceMapBuilder(sourceMapUri, fileUri, lineColumnProvider);
1986 output.forEachSourceLocation(sourceMapBuilder.addMapping);
1987 String sourceMap = sourceMapBuilder.build();
1988 compiler.outputProvider(name, 'js.map')
1989 ..add(sourceMap)
1990 ..close();
1991 }
1992
1993 void outputDeferredMap() {
1994 Map<String, dynamic> mapping = new Map<String, dynamic>();
1995 // Json does not support comments, so we embed the explanation in the
1996 // data.
1997 mapping["_comment"] = "This mapping shows which compiled `.js` files are "
1998 "needed for a given deferred library import.";
1999 mapping.addAll(compiler.deferredLoadTask.computeDeferredMap());
2000 compiler.outputProvider(compiler.deferredMapUri.path, 'deferred_map')
2001 ..add(const JsonEncoder.withIndent(" ").convert(mapping))
2002 ..close();
2003 }
2004
2005 void invalidateCaches() {
2006 if (!compiler.hasIncrementalSupport) return;
2007 if (cachedElements.isEmpty) return;
2008 for (Element element in compiler.enqueuer.codegen.newlyEnqueuedElements) {
2009 if (element.isInstanceMember) {
2010 cachedClassBuilders.remove(element.enclosingClass);
2011
2012 nativeEmitter.cachedBuilders.remove(element.enclosingClass);
2013
2014 }
2015 }
2016 }
2017 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698