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

Side by Side Diff: pkg/compiler/lib/src/js_emitter/new_emitter/model_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 library dart2js.new_js_emitter.model_emitter;
6
7 import '../../constants/values.dart' show ConstantValue, FunctionConstantValue;
8 import '../../dart2jslib.dart' show Compiler;
9 import '../../elements/elements.dart' show ClassElement, FunctionElement;
10 import '../../js/js.dart' as js;
11 import '../../js_backend/js_backend.dart' show
12 JavaScriptBackend,
13 Namer,
14 ConstantEmitter;
15
16 import '../js_emitter.dart' show AstContainer, NativeEmitter;
17
18 import 'package:js_runtime/shared/embedded_names.dart' show
19 CREATE_NEW_ISOLATE,
20 DEFERRED_LIBRARY_URIS,
21 DEFERRED_LIBRARY_HASHES,
22 GET_TYPE_FROM_NAME,
23 INITIALIZE_LOADED_HUNK,
24 INTERCEPTORS_BY_TAG,
25 IS_HUNK_INITIALIZED,
26 IS_HUNK_LOADED,
27 LEAF_TAGS,
28 MANGLED_GLOBAL_NAMES,
29 METADATA,
30 TYPE_TO_INTERCEPTOR_MAP,
31 TYPES;
32
33 import '../js_emitter.dart' show NativeGenerator, buildTearOffCode;
34 import '../model.dart';
35
36 class ModelEmitter {
37 final Compiler compiler;
38 final Namer namer;
39 ConstantEmitter constantEmitter;
40 final NativeEmitter nativeEmitter;
41
42 JavaScriptBackend get backend => compiler.backend;
43
44 /// For deferred loading we communicate the initializers via this global var.
45 static const String deferredInitializersGlobal =
46 r"$__dart_deferred_initializers__";
47
48 static const String deferredExtension = "part.js";
49
50 static const String typeNameProperty = r"builtin$cls";
51
52 ModelEmitter(Compiler compiler, Namer namer, this.nativeEmitter)
53 : this.compiler = compiler,
54 this.namer = namer {
55
56 this.constantEmitter = new ConstantEmitter(
57 compiler, namer, this.generateConstantReference,
58 constantListGenerator);
59 }
60
61 js.Expression constantListGenerator(js.Expression array) {
62 // TODO(floitsch): remove hard-coded name.
63 return js.js('makeConstList(#)', [array]);
64 }
65
66 js.Expression generateEmbeddedGlobalAccess(String global) {
67 return js.js(generateEmbeddedGlobalAccessString(global));
68 }
69
70 String generateEmbeddedGlobalAccessString(String global) {
71 // TODO(floitsch): don't use 'init' as global embedder storage.
72 return 'init.$global';
73 }
74
75 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) {
76 if (constant.isFunction) return true; // Already emitted.
77 if (constant.isPrimitive) return true; // Inlined.
78 if (constant.isDummy) return true; // Inlined.
79 // The name is null when the constant is already a JS constant.
80 // TODO(floitsch): every constant should be registered, so that we can
81 // share the ones that take up too much space (like some strings).
82 if (namer.constantName(constant) == null) return true;
83 return false;
84 }
85
86 // TODO(floitsch): copied from OldEmitter. Adjust or share.
87 int compareConstants(ConstantValue a, ConstantValue b) {
88 // Inlined constants don't affect the order and sometimes don't even have
89 // names.
90 int cmp1 = isConstantInlinedOrAlreadyEmitted(a) ? 0 : 1;
91 int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1;
92 if (cmp1 + cmp2 < 2) return cmp1 - cmp2;
93
94 // Emit constant interceptors first. Constant interceptors for primitives
95 // might be used by code that builds other constants. See Issue 18173.
96 if (a.isInterceptor != b.isInterceptor) {
97 return a.isInterceptor ? -1 : 1;
98 }
99
100 // Sorting by the long name clusters constants with the same constructor
101 // which compresses a tiny bit better.
102 int r = namer.constantLongName(a).compareTo(namer.constantLongName(b));
103 if (r != 0) return r;
104 // Resolve collisions in the long name by using the constant name (i.e. JS
105 // name) which is unique.
106 return namer.constantName(a).compareTo(namer.constantName(b));
107 }
108
109 js.Expression generateStaticClosureAccess(FunctionElement element) {
110 return js.js('#.#()',
111 [namer.globalObjectFor(element), namer.staticClosureName(element)]);
112 }
113
114 js.Expression generateConstantReference(ConstantValue value) {
115 if (value.isFunction) {
116 FunctionConstantValue functionConstant = value;
117 return generateStaticClosureAccess(functionConstant.element);
118 }
119
120 // We are only interested in the "isInlined" part, but it does not hurt to
121 // test for the other predicates.
122 if (isConstantInlinedOrAlreadyEmitted(value)) {
123 return constantEmitter.generate(value);
124 }
125 return js.js('#.#()', [namer.globalObjectForConstant(value),
126 namer.constantName(value)]);
127 }
128
129 int emitProgram(Program program) {
130 List<Fragment> fragments = program.fragments;
131 MainFragment mainFragment = fragments.first;
132
133 int totalSize = 0;
134
135 // We have to emit the deferred fragments first, since we need their
136 // deferred hash (which depends on the output) when emitting the main
137 // fragment.
138 List<js.Expression> fragmentsCode = fragments.skip(1).map(
139 (DeferredFragment deferredUnit) {
140 js.Expression types =
141 program.metadataTypesForOutputUnit(deferredUnit.outputUnit);
142 return emitDeferredFragment(types, deferredUnit, program.holders);
143 }).toList();
144
145 js.Statement mainAst = emitMainFragment(program);
146
147 js.TokenCounter counter = new js.TokenCounter();
148 fragmentsCode.forEach(counter.countTokens);
149 counter.countTokens(mainAst);
150
151 program.finalizers.forEach((js.TokenFinalizer f) => f.finalizeTokens());
152
153 for (int i = 0; i < fragmentsCode.length; ++i) {
154 String code = js.prettyPrint(fragmentsCode[i], compiler).getText();
155 totalSize += code.length;
156 compiler.outputProvider(fragments[i+1].outputFileName, deferredExtension)
157 ..add(code)
158 ..close();
159 }
160
161 String mainCode = js.prettyPrint(mainAst, compiler).getText();
162 compiler.outputProvider(mainFragment.outputFileName, 'js')
163 ..add(buildGeneratedBy(compiler))
164 ..add(mainCode)
165 ..close();
166 totalSize += mainCode.length;
167
168 return totalSize;
169 }
170
171 /// Returns a [js.Literal] that represents the string result of unparsing
172 /// [value].
173 ///
174 /// The returned node will, when its string value is requested, pretty-print
175 /// the given [value] and, if [protectForEval] is true, wrap the resulting
176 /// string in parenthesis. The result is also escaped.
177 ///
178 /// See [_UnparsedNode] for details.
179 js.Literal unparse(Compiler compiler, js.Node value,
180 {bool protectForEval: true}) {
181 return new js.UnparsedNode(value, compiler, protectForEval);
182 }
183
184 String buildGeneratedBy(compiler) {
185 var suffix = '';
186 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
187 return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n';
188 }
189
190 js.Statement emitMainFragment(Program program) {
191 MainFragment fragment = program.fragments.first;
192 List<js.Expression> elements = fragment.libraries.map(emitLibrary).toList();
193 elements.add(
194 emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields));
195 elements.add(emitConstants(fragment.constants));
196
197 js.Expression code = new js.ArrayInitializer(elements);
198
199 Map<String, dynamic> holes =
200 {'deferredInitializer': emitDeferredInitializerGlobal(program.loadMap),
201 'holders': emitHolders(program.holders),
202 'tearOff': buildTearOffCode(backend),
203 'parseFunctionDescriptor':
204 js.js.statement(parseFunctionDescriptorBoilerplate,
205 {'argumentCount': js.string(namer.requiredParameterField),
206 'defaultArgumentValues': js.string(namer.defaultValuesField),
207 'callName': js.string(namer.callNameField)}),
208
209 'cyclicThrow':
210 backend.emitter.staticFunctionAccess(backend.getCyclicThrowHelper()),
211 'outputContainsConstantList': program.outputContainsConstantList,
212 'embeddedGlobals': emitEmbeddedGlobals(program),
213 'readMetadataTypeFunction': readMetadataTypeFunction,
214 'staticNonFinals':
215 emitStaticNonFinalFields(fragment.staticNonFinalFields),
216 'operatorIsPrefix': js.string(namer.operatorIsPrefix),
217 'callName': js.string(namer.callNameField),
218 'argumentCount': js.string(namer.requiredParameterField),
219 'defaultArgumentValues': js.string(namer.defaultValuesField),
220 'eagerClasses': emitEagerClassInitializations(fragment.libraries),
221 'invokeMain': fragment.invokeMain,
222 'code': code};
223
224 holes.addAll(nativeHoles(program));
225
226 return js.js.statement(boilerplate, holes);
227 }
228
229 Map<String, dynamic> nativeHoles(Program program) {
230 Map<String, dynamic> nativeHoles = <String, dynamic>{};
231
232 js.Statement nativeIsolateAffinityTagInitialization;
233 if (NativeGenerator.needsIsolateAffinityTagInitialization(backend)) {
234 nativeIsolateAffinityTagInitialization =
235 NativeGenerator.generateIsolateAffinityTagInitialization(
236 backend,
237 generateEmbeddedGlobalAccess,
238 // TODO(floitsch): convertToFastObject.
239 js.js("(function(x) { return x; })", []));
240 } else {
241 nativeIsolateAffinityTagInitialization = js.js.statement(";");
242 }
243 nativeHoles['nativeIsolateAffinityTagInitialization'] =
244 nativeIsolateAffinityTagInitialization;
245
246
247 js.Expression nativeInfoAccess = js.js('nativeInfo', []);
248 js.Expression constructorAccess = js.js('constructor', []);
249 Function subclassReadGenerator = (js.Expression subclass) {
250 return js.js('holdersMap[#][#].ensureResolved()', [subclass, subclass]);
251 };
252 js.Expression interceptorsByTagAccess =
253 generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG);
254 js.Expression leafTagsAccess =
255 generateEmbeddedGlobalAccess(LEAF_TAGS);
256 js.Statement nativeInfoHandler = NativeGenerator.buildNativeInfoHandler(
257 nativeInfoAccess,
258 constructorAccess,
259 subclassReadGenerator,
260 interceptorsByTagAccess,
261 leafTagsAccess);
262
263 nativeHoles['needsNativeSupport'] = program.needsNativeSupport;
264 nativeHoles['needsNoNativeSupport'] = !program.needsNativeSupport;
265 nativeHoles['nativeInfoHandler'] = nativeInfoHandler;
266
267 return nativeHoles;
268 }
269
270 js.Block emitHolders(List<Holder> holders) {
271 // The top-level variables for holders must *not* be renamed by the
272 // JavaScript pretty printer because a lot of code already uses the
273 // non-renamed names. The generated code looks like this:
274 //
275 // var H = {}, ..., G = {};
276 // var holders = [ H, ..., G ];
277 //
278 // and it is inserted at the top of the top-level function expression
279 // that covers the entire program.
280
281 List<js.Statement> statements = [
282 new js.ExpressionStatement(
283 new js.VariableDeclarationList(holders.map((e) =>
284 new js.VariableInitialization(
285 new js.VariableDeclaration(e.name, allowRename: false),
286 new js.ObjectInitializer(const []))).toList())),
287 js.js.statement('var holders = #', new js.ArrayInitializer(
288 holders.map((e) => new js.VariableUse(e.name))
289 .toList(growable: false))),
290 js.js.statement('var holdersMap = Object.create(null)')
291 ];
292 return new js.Block(statements);
293 }
294
295 js.Block emitEmbeddedGlobals(Program program) {
296 List<js.Property> globals = <js.Property>[];
297
298 if (program.loadMap.isNotEmpty) {
299 globals.addAll(emitEmbeddedGlobalsForDeferredLoading(program.loadMap));
300 }
301
302 if (program.typeToInterceptorMap != null) {
303 globals.add(new js.Property(js.string(TYPE_TO_INTERCEPTOR_MAP),
304 program.typeToInterceptorMap));
305 }
306
307 if (program.hasIsolateSupport) {
308 String isolateName = namer.currentIsolate;
309 globals.add(
310 new js.Property(js.string(CREATE_NEW_ISOLATE),
311 js.js('function () { return $isolateName; }')));
312 // TODO(floitsch): add remaining isolate functions.
313 }
314
315 globals.add(emitMangledGlobalNames());
316
317 globals.add(emitGetTypeFromName());
318
319 globals.addAll(emitMetadata(program));
320
321 if (program.needsNativeSupport) {
322 globals.add(new js.Property(js.string(INTERCEPTORS_BY_TAG),
323 js.js('Object.create(null)', [])));
324 globals.add(new js.Property(js.string(LEAF_TAGS),
325 js.js('Object.create(null)', [])));
326 }
327
328 js.ObjectInitializer globalsObject = new js.ObjectInitializer(globals);
329
330 List<js.Statement> statements =
331 [new js.ExpressionStatement(
332 new js.VariableDeclarationList(
333 [new js.VariableInitialization(
334 new js.VariableDeclaration("init", allowRename: false),
335 globalsObject)]))];
336 return new js.Block(statements);
337 }
338
339 js.Property emitMangledGlobalNames() {
340 List<js.Property> names = <js.Property>[];
341
342 // We want to keep the original names for the most common core classes when
343 // calling toString on them.
344 List<ClassElement> nativeClassesNeedingUnmangledName =
345 [compiler.intClass, compiler.doubleClass, compiler.numClass,
346 compiler.stringClass, compiler.boolClass, compiler.nullClass,
347 compiler.listClass];
348 nativeClassesNeedingUnmangledName.forEach((element) {
349 names.add(new js.Property(js.quoteName(namer.className(element)),
350 js.string(element.name)));
351 });
352
353 return new js.Property(js.string(MANGLED_GLOBAL_NAMES),
354 new js.ObjectInitializer(names));
355 }
356
357 js.Statement emitDeferredInitializerGlobal(Map loadMap) {
358 if (loadMap.isEmpty) return new js.Block.empty();
359
360 return js.js.statement("""
361 if (typeof($deferredInitializersGlobal) === 'undefined')
362 var $deferredInitializersGlobal = Object.create(null);""");
363 }
364
365 Iterable<js.Property> emitEmbeddedGlobalsForDeferredLoading(
366 Map<String, List<Fragment>> loadMap) {
367
368 List<js.Property> globals = <js.Property>[];
369
370 js.ArrayInitializer fragmentUris(List<Fragment> fragments) {
371 return js.stringArray(fragments.map((DeferredFragment fragment) =>
372 "${fragment.outputFileName}.$deferredExtension"));
373 }
374 js.ArrayInitializer fragmentHashes(List<Fragment> fragments) {
375 // TODO(floitsch): the hash must depend on the generated code.
376 return js.numArray(
377 fragments.map((DeferredFragment fragment) => fragment.hashCode));
378 }
379
380 List<js.Property> uris = new List<js.Property>(loadMap.length);
381 List<js.Property> hashes = new List<js.Property>(loadMap.length);
382 int count = 0;
383 loadMap.forEach((String loadId, List<Fragment> fragmentList) {
384 uris[count] =
385 new js.Property(js.string(loadId), fragmentUris(fragmentList));
386 hashes[count] =
387 new js.Property(js.string(loadId), fragmentHashes(fragmentList));
388 count++;
389 });
390
391 globals.add(new js.Property(js.string(DEFERRED_LIBRARY_URIS),
392 new js.ObjectInitializer(uris)));
393 globals.add(new js.Property(js.string(DEFERRED_LIBRARY_HASHES),
394 new js.ObjectInitializer(hashes)));
395
396 js.Expression isHunkLoadedFunction =
397 js.js("function(hash) { return !!$deferredInitializersGlobal[hash]; }");
398 globals.add(new js.Property(js.string(IS_HUNK_LOADED),
399 isHunkLoadedFunction));
400
401 js.Expression isHunkInitializedFunction =
402 js.js("function(hash) { return false; }");
403 globals.add(new js.Property(js.string(IS_HUNK_INITIALIZED),
404 isHunkInitializedFunction));
405
406 js.Expression typesAccess = generateEmbeddedGlobalAccess(TYPES);
407
408 /// See [emitEmbeddedGlobalsForDeferredLoading] for the format of the
409 /// deferred hunk.
410 js.Expression initializeLoadedHunkFunction =
411 js.js("""
412 function(hash) {
413 var hunk = $deferredInitializersGlobal[hash];
414 $setupProgramName(hunk[0], #typesAccess.length);
415 eval(hunk[1]);
416 var deferredTypes = eval(hunk[2]);
417 #typesAccess.push.apply(#typesAccess, deferredTypes);
418 }""", {'typesAccess': typesAccess});
419
420 globals.add(new js.Property(js.string(INITIALIZE_LOADED_HUNK),
421 initializeLoadedHunkFunction));
422
423 return globals;
424 }
425
426 js.Property emitGetTypeFromName() {
427 js.Expression function =
428 js.js( """function(name) {
429 return holdersMap[name][name].ensureResolved();
430 }""");
431 return new js.Property(js.string(GET_TYPE_FROM_NAME), function);
432 }
433
434 static final String readMetadataTypeName = "readMetadataType";
435
436 js.Statement get readMetadataTypeFunction {
437 // Types are non-evaluated and must be compiled at first use.
438 // Compiled strings are guaranteed not to be strings, and it's thus safe
439 // to use a type-test to determine if a type has already been compiled.
440 return js.js.statement('''function $readMetadataTypeName(index) {
441 var type = #typesAccess[index];
442 if (typeof type == 'string') {
443 type = expressionCompile(type);
444 #typesAccess[index] = type;
445 }
446 return type;
447 }''', {"typesAccess": generateEmbeddedGlobalAccess(TYPES)});
448 }
449
450 js.Template get templateForReadType {
451 // TODO(floitsch): make sure that no local variable shadows the access to
452 // the readMetadataType function.
453 return js.js.expressionTemplateFor('$readMetadataTypeName(#)');
454 }
455
456 static final String readMetadataName = "readLazyMetadata";
457 static final String lazyMetadataName = "lazy_$METADATA";
458
459 js.Statement get readMetadataFunction {
460 // Types are non-evaluated and must be compiled at first use.
461 // Compiled strings are guaranteed not to be strings, and it's thus safe
462 // to use a type-test to determine if a type has already been compiled.
463 return js.js.statement('''function $readMetadataName(index) {
464 var lazyMetadata = #lazyMetadataAccess[index];
465 if (typeof lazyMetadata == 'string') {
466 #metadataAccess[index] = expressionCompile(lazyMetadata);
467 #lazyMetadataAccess[index] = null;
468 }
469 return #metadataAccess[index];
470 }''', {
471 "lazyMetadataAccess": generateEmbeddedGlobalAccess(lazyMetadataName),
472 "metadataAccess": generateEmbeddedGlobalAccess(METADATA)
473 });
474 }
475
476 js.Template get templateForReadMetadata {
477 // TODO(floitsch): make sure that no local variable shadows the access to
478 // the readMetadata function.
479 return js.js.expressionTemplateFor('$readMetadataName(#)');
480 }
481
482 List<js.Property> emitMetadata(Program program) {
483 List<js.Property> metadataGlobals = <js.Property>[];
484
485 js.Property createGlobal(js.Expression metadata, String global) {
486 return new js.Property(js.string(global), metadata);
487 }
488
489 metadataGlobals.add(createGlobal(program.metadata, METADATA));
490 js.Expression types =
491 program.metadataTypesForOutputUnit(program.mainFragment.outputUnit);
492 metadataGlobals.add(createGlobal(types, TYPES));
493
494 return metadataGlobals;
495 }
496
497 js.Expression emitDeferredFragment(js.Expression deferredTypes,
498 DeferredFragment fragment,
499 List<Holder> holders) {
500 // TODO(floitsch): initialize eager classes.
501 // TODO(floitsch): the hash must depend on the output.
502 int hash = fragment.hashCode;
503
504 List<js.Expression> deferredCode =
505 fragment.libraries.map(emitLibrary).toList();
506
507 deferredCode.add(
508 emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields));
509 deferredCode.add(emitConstants(fragment.constants));
510
511 js.ArrayInitializer deferredArray = new js.ArrayInitializer(deferredCode);
512
513 // This is the code that must be evaluated after all deferred classes have
514 // been setup.
515 js.Statement immediateCode = new js.Block([
516 emitStaticNonFinalFields(fragment.staticNonFinalFields),
517 emitEagerClassInitializations(fragment.libraries)]);
518
519
520 js.Literal immediateString = unparse(compiler, immediateCode);
521
522 js.ArrayInitializer hunk =
523 new js.ArrayInitializer([deferredArray, immediateString,
524 deferredTypes]);
525
526 return js.js("$deferredInitializersGlobal[$hash] = #", hunk);
527 }
528
529 // This string should be referenced wherever JavaScript code makes assumptions
530 // on the constants format.
531 static final String constantsDescription =
532 "The constants are encoded as a follows:"
533 " [constantsHolderIndex, name, code, name2, code2, ...]."
534 "The array is completely empty if there is no constant at all.";
535
536 js.ArrayInitializer emitConstants(List<Constant> constants) {
537 List<js.Expression> data = <js.Expression>[];
538 if (constants.isNotEmpty) {
539 int holderIndex = constants.first.holder.index;
540 data.add(js.number(holderIndex));
541 data.addAll(constants.expand((Constant constant) {
542 assert(constant.holder.index == holderIndex);
543 js.Expression code = constantEmitter.generate(constant.value);
544 return [js.quoteName(constant.name), unparse(compiler, code)];
545 }));
546 }
547 return new js.ArrayInitializer(data);
548 }
549
550 js.Block emitStaticNonFinalFields(List<StaticField> fields) {
551 Iterable<js.Statement> statements = fields.map((StaticField field) {
552 return js.js.statement("#.# = #;",
553 [field.holder.name, field.name, field.code]);
554 });
555 return new js.Block(statements.toList());
556 }
557
558 js.Expression emitLazilyInitializedStatics(List<StaticField> fields) {
559 Iterable fieldDescriptors = fields.expand((field) =>
560 [ js.quoteName(field.name),
561 js.quoteName(namer.deriveLazyInitializerName(field.name)),
562 js.number(field.holder.index),
563 emitLazyInitializer(field) ]);
564 return new js.ArrayInitializer(fieldDescriptors.toList(growable: false));
565 }
566
567 js.Block emitEagerClassInitializations(List<Library> libraries) {
568 js.Statement createInstantiation(Class cls) {
569 return js.js.statement('new #.#()', [cls.holder.name, cls.name]);
570 }
571
572 List<js.Statement> instantiations =
573 libraries.expand((Library library) => library.classes)
574 .where((Class cls) => cls.isEager)
575 .map(createInstantiation)
576 .toList(growable: false);
577 return new js.Block(instantiations);
578 }
579
580 // This string should be referenced wherever JavaScript code makes assumptions
581 // on the mixin format.
582 static final String nativeInfoDescription =
583 "A class is encoded as follows:"
584 " [name, class-code, holder-index], or "
585 " [name, class-code, native-info, holder-index].";
586
587 js.Expression emitLibrary(Library library) {
588 Iterable staticDescriptors = library.statics.expand(emitStaticMethod);
589
590 Iterable classDescriptors = library.classes.expand((Class cls) {
591 js.Literal name = js.quoteName(cls.name);
592 js.LiteralNumber holderIndex = js.number(cls.holder.index);
593 js.Expression emittedClass = emitClass(cls);
594 js.Expression nativeInfo = NativeGenerator.encodeNativeInfo(cls);
595 if (nativeInfo == null) {
596 return [name, emittedClass, holderIndex];
597 } else {
598 return [name, emittedClass, nativeInfo, holderIndex];
599 }
600 });
601
602 js.Expression staticArray =
603 new js.ArrayInitializer(staticDescriptors.toList(growable: false));
604 js.Expression classArray =
605 new js.ArrayInitializer(classDescriptors.toList(growable: false));
606
607 return new js.ArrayInitializer([staticArray, classArray]);
608 }
609
610 js.Expression _generateConstructor(Class cls) {
611 List<js.Name> fieldNames = const <js.Name>[];
612
613 // If the class is not directly instantiated we only need it for inheritance
614 // or RTI. In either case we don't need its fields.
615 if (cls.isDirectlyInstantiated && !cls.isNative) {
616 fieldNames = cls.fields.map((Field field) => field.name).toList();
617 }
618 js.Name name = cls.name;
619
620 Iterable<js.Name> assignments = fieldNames.map((js.Name field) {
621 return js.js("this.#field = #field", {"field": field});
622 });
623
624 return js.js('function #(#) { # }', [name, fieldNames, assignments]);
625 }
626
627 Method _generateGetter(Field field) {
628 String getterTemplateFor(int flags) {
629 switch (flags) {
630 case 1: return "function() { return this[#]; }";
631 case 2: return "function(receiver) { return receiver[#]; }";
632 case 3: return "function(receiver) { return this[#]; }";
633 }
634 return null;
635 }
636
637 js.Expression fieldName = js.quoteName(field.name);
638 js.Expression code = js.js(getterTemplateFor(field.getterFlags), fieldName);
639 js.Name getterName = namer.deriveGetterName(field.accessorName);
640 return new StubMethod(getterName, code);
641 }
642
643 Method _generateSetter(Field field) {
644 String setterTemplateFor(int flags) {
645 switch (flags) {
646 case 1: return "function(val) { return this[#] = val; }";
647 case 2: return "function(receiver, val) { return receiver[#] = val; }";
648 case 3: return "function(receiver, val) { return this[#] = val; }";
649 }
650 return null;
651 }
652 js.Expression fieldName = js.quoteName(field.name);
653 js.Expression code = js.js(setterTemplateFor(field.setterFlags), fieldName);
654 js.Name setterName = namer.deriveSetterName(field.accessorName);
655 return new StubMethod(setterName, code);
656 }
657
658 Iterable<Method> _generateGettersSetters(Class cls) {
659 Iterable<Method> getters = cls.fields
660 .where((Field field) => field.needsGetter)
661 .map(_generateGetter);
662
663 Iterable<Method> setters = cls.fields
664 .where((Field field) => field.needsUncheckedSetter)
665 .map(_generateSetter);
666
667 return [getters, setters].expand((x) => x);
668 }
669
670 // This string should be referenced wherever JavaScript code makes assumptions
671 // on the mixin format.
672 static final String mixinFormatDescription =
673 "Mixins have a reference to their mixin class at the place of the usual"
674 "constructor. If they are instantiated the constructor follows the"
675 "reference.";
676
677 js.Expression emitClass(Class cls) {
678 List elements = [js.quoteName(cls.superclassName, allowNull: true),
679 js.number(cls.superclassHolderIndex)];
680
681 if (cls.isMixinApplication) {
682 MixinApplication mixin = cls;
683 elements.add(js.quoteName(mixin.mixinClass.name));
684 elements.add(js.number(mixin.mixinClass.holder.index));
685 if (cls.isDirectlyInstantiated) {
686 elements.add(_generateConstructor(cls));
687 }
688 } else {
689 elements.add(_generateConstructor(cls));
690 }
691 Iterable<Method> methods = cls.methods;
692 Iterable<Method> isChecks = cls.isChecks;
693 Iterable<Method> callStubs = cls.callStubs;
694 Iterable<Method> typeVariableReaderStubs = cls.typeVariableReaderStubs;
695 Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs;
696 Iterable<Method> gettersSetters = _generateGettersSetters(cls);
697 Iterable<Method> allMethods =
698 [methods, isChecks, callStubs, typeVariableReaderStubs,
699 noSuchMethodStubs, gettersSetters].expand((x) => x);
700 elements.addAll(allMethods.expand(emitInstanceMethod));
701
702 return unparse(compiler, new js.ArrayInitializer(elements));
703 }
704
705 js.Expression emitLazyInitializer(StaticField field) {
706 assert(field.isLazy);
707 return unparse(compiler, field.code);
708 }
709
710 /// JavaScript code template that implements parsing of a function descriptor.
711 /// Descriptors are used in place of the actual JavaScript function
712 /// definition in the output if additional information needs to be passed to
713 /// facilitate the generation of tearOffs at runtime. The format is an array
714 /// with the following fields:
715 ///
716 /// * [InstanceMethod.aliasName] (optional).
717 /// * [Method.code]
718 /// * [DartMethod.callName]
719 /// * isInterceptedMethod (optional, present if [DartMethod.needsTearOff]).
720 /// * [DartMethod.tearOffName] (optional, present if
721 /// [DartMethod.needsTearOff]).
722 /// * functionType (optional, present if [DartMethod.needsTearOff]).
723 ///
724 /// followed by
725 ///
726 /// * [ParameterStubMethod.name]
727 /// * [ParameterStubMethod.callName]
728 /// * [ParameterStubMethod.code]
729 ///
730 /// for each stub in [DartMethod.parameterStubs].
731 ///
732 /// If the closure could be used in `Function.apply` (i.e.
733 /// [DartMethod.canBeApplied] is true) then the following fields are appended:
734 ///
735 /// * [DartMethod.requiredParameterCount]
736 /// * [DartMethod.optionalParameterDefaultValues]
737
738 static final String parseFunctionDescriptorBoilerplate = r"""
739 function parseFunctionDescriptor(proto, name, descriptor, typesOffset) {
740 if (descriptor instanceof Array) {
741 // 'pos' points to the last read entry.
742 var f, pos = -1;
743 var aliasOrFunction = descriptor[++pos];
744 if (typeof aliasOrFunction == "string") {
745 // Install the alias for super calls on the prototype chain.
746 proto[aliasOrFunction] = f = descriptor[++pos];
747 } else {
748 f = aliasOrFunction;
749 }
750
751 proto[name] = f;
752 var funs = [f];
753 f[#callName] = descriptor[++pos];
754
755 var isInterceptedOrParameterStubName = descriptor[pos + 1];
756 var isIntercepted, tearOffName, reflectionInfo;
757 if (typeof isInterceptedOrParameterStubName == "boolean") {
758 isIntercepted = descriptor[++pos];
759 tearOffName = descriptor[++pos];
760 reflectionInfo = descriptor[++pos];
761 if (typeof reflectionInfo == "number") {
762 reflectionInfo = reflectionInfo + typesOffset;
763 }
764 }
765
766 // We iterate in blocks of 3 but have to stop before we reach the (optional)
767 // two trailing items. To accomplish this, we only iterate until we reach
768 // length - 2.
769 for (++pos; pos < descriptor.length - 2; pos += 3) {
770 var stub = descriptor[pos + 2];
771 stub[#callName] = descriptor[pos + 1];
772 proto[descriptor[pos]] = stub;
773 funs.push(stub);
774 }
775
776 if (tearOffName) {
777 proto[tearOffName] =
778 tearOff(funs, reflectionInfo, false, name, isIntercepted);
779 }
780 if (pos < descriptor.length) {
781 f[#argumentCount] = descriptor[pos];
782 f[#defaultArgumentValues] = descriptor[pos + 1];
783 }
784 } else {
785 proto[name] = descriptor;
786 }
787 }
788 """;
789
790 js.Expression _encodeOptionalParameterDefaultValues(DartMethod method) {
791 // TODO(herhut): Replace [js.LiteralNull] with [js.ArrayHole].
792 if (method.optionalParameterDefaultValues is List) {
793 List<ConstantValue> defaultValues = method.optionalParameterDefaultValues;
794 Iterable<js.Expression> elements =
795 defaultValues.map(generateConstantReference);
796 return new js.ArrayInitializer(elements.toList());
797 } else {
798 Map<String, ConstantValue> defaultValues =
799 method.optionalParameterDefaultValues;
800 List<js.Property> properties = <js.Property>[];
801 defaultValues.forEach((String name, ConstantValue value) {
802 properties.add(new js.Property(js.string(name),
803 generateConstantReference(value)));
804 });
805 return new js.ObjectInitializer(properties);
806 }
807 }
808
809 Iterable<js.Expression> emitInstanceMethod(Method method) {
810
811 List<js.Expression> makeNameCodePair(Method method) {
812 return [js.quoteName(method.name), method.code];
813 }
814
815 List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) {
816 js.Expression callName = stub.callName == null
817 ? new js.LiteralNull()
818 : js.quoteName(stub.callName);
819 return [js.quoteName(stub.name), callName, stub.code];
820 }
821
822 if (method is InstanceMethod) {
823 if (method.needsTearOff || method.aliasName != null) {
824 /// See [parseFunctionDescriptorBoilerplate] for a full description of
825 /// the format.
826 // [name, [aliasName, function, callName, isIntercepted, tearOffName,
827 // functionType, stub1_name, stub1_callName, stub1_code, ...]
828 var data = [];
829 if (method.aliasName != null) {
830 data.add(js.quoteName(method.aliasName));
831 }
832 data.add(method.code);
833 data.add(js.quoteName(method.callName, allowNull: true));
834
835 if (method.needsTearOff) {
836 bool isIntercepted = backend.isInterceptedMethod(method.element);
837 data.add(new js.LiteralBool(isIntercepted));
838 data.add(js.quoteName(method.tearOffName));
839 data.add((method.functionType));
840 }
841
842 data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet));
843 if (method.canBeApplied) {
844 data.add(js.number(method.requiredParameterCount));
845 data.add(_encodeOptionalParameterDefaultValues(method));
846 }
847 return [js.quoteName(method.name), new js.ArrayInitializer(data)];
848 } else {
849 // TODO(floitsch): not the most efficient way...
850 return ([method]..addAll(method.parameterStubs))
851 .expand(makeNameCodePair);
852 }
853 } else {
854 return makeNameCodePair(method);
855 }
856 }
857
858 Iterable<js.Expression> emitStaticMethod(StaticMethod method) {
859 js.Expression holderIndex = js.number(method.holder.index);
860 List<js.Expression> output = <js.Expression>[];
861
862 void _addMethod(Method method) {
863 js.Expression unparsed = unparse(compiler, method.code);
864 output.add(js.quoteName(method.name));
865 output.add(holderIndex);
866 output.add(unparsed);
867 }
868
869 List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) {
870 js.Expression callName = stub.callName == null
871 ? new js.LiteralNull()
872 : js.quoteName(stub.callName);
873 return [js.quoteName(stub.name), callName, unparse(compiler, stub.code)];
874 }
875
876 _addMethod(method);
877 // TODO(floitsch): can there be anything else than a StaticDartMethod?
878 if (method is StaticDartMethod) {
879 if (method.needsTearOff) {
880 /// The format emitted is the same as for the parser specified at
881 /// [parseFunctionDescriptorBoilerplate] except for the missing
882 /// field whether the method is intercepted.
883 // [name, [function, callName, tearOffName, functionType,
884 // stub1_name, stub1_callName, stub1_code, ...]
885 var data = [unparse(compiler, method.code)];
886 data.add(js.quoteName(method.callName));
887 data.add(js.quoteName(method.tearOffName));
888 data.add(method.functionType);
889 data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet));
890 if (method.canBeApplied) {
891 data.add(js.number(method.requiredParameterCount));
892 data.add(_encodeOptionalParameterDefaultValues(method));
893 }
894 return [js.quoteName(method.name), holderIndex,
895 new js.ArrayInitializer(data)];
896 } else {
897 method.parameterStubs.forEach(_addMethod);
898 }
899 }
900 return output;
901 }
902
903 static final String setupProgramName = "setupProgram";
904
905 static final String boilerplate = """
906 {
907 // Declare deferred-initializer global.
908 #deferredInitializer;
909
910 (function(start, program) {
911 // Initialize holder objects.
912 #holders;
913 var nativeInfos = Object.create(null);
914
915 // Counter to generate unique names for tear offs.
916 var functionCounter = 0;
917
918 function $setupProgramName(program, typesOffset) {
919 for (var i = 0; i < program.length - 2; i++) {
920 setupLibrary(program[i], typesOffset);
921 }
922 setupLazyStatics(program[i]);
923 setupConstants(program[i + 1]);
924 }
925
926 function setupLibrary(library, typesOffset) {
927 var statics = library[0];
928 for (var i = 0; i < statics.length; i += 3) {
929 var holderIndex = statics[i + 1];
930 setupStatic(statics[i], holders[holderIndex], statics[i + 2],
931 typesOffset);
932 }
933
934 var classes = library[1];
935 for (var i = 0; i < classes.length; i += 3) {
936 var name = classes[i];
937 var cls = classes[i + 1];
938
939 if (#needsNativeSupport) {
940 // $nativeInfoDescription.
941 var indexOrNativeInfo = classes[i + 2];
942 if (typeof indexOrNativeInfo == "number") {
943 var holderIndex = classes[i + 2];
944 } else {
945 nativeInfos[name] = indexOrNativeInfo;
946 holderIndex = classes[i + 3];
947 i++;
948 }
949 }
950
951 if (#needsNoNativeSupport) {
952 var holderIndex = classes[i + 2];
953 }
954
955 holdersMap[name] = holders[holderIndex];
956 setupClass(name, holders[holderIndex], cls, typesOffset);
957 }
958 }
959
960 function setupLazyStatics(statics) {
961 for (var i = 0; i < statics.length; i += 4) {
962 var name = statics[i];
963 var getterName = statics[i + 1];
964 var holderIndex = statics[i + 2];
965 var initializer = statics[i + 3];
966 setupLazyStatic(name, getterName, holders[holderIndex], initializer);
967 }
968 }
969
970 function setupConstants(constants) {
971 // $constantsDescription.
972 if (constants.length == 0) return;
973 // We assume that all constants are in the same holder.
974 var holder = holders[constants[0]];
975 for (var i = 1; i < constants.length; i += 2) {
976 var name = constants[i];
977 var initializer = constants[i + 1];
978 setupConstant(name, holder, initializer);
979 }
980 }
981
982 function setupStatic(name, holder, descriptor, typesOffset) {
983 if (typeof descriptor == 'string') {
984 holder[name] = function() {
985 if (descriptor == null) {
986 // Already compiled. This happens when we have calls to the static as
987 // arguments to the static: `foo(foo(499))`;
988 return holder[name].apply(this, arguments);
989 }
990 var method = compile(name, descriptor);
991 holder[name] = method;
992 descriptor = null; // GC the descriptor.
993 return method.apply(this, arguments);
994 };
995 } else {
996 // Parse the tear off information and generate compile handlers.
997 // TODO(herhut): Share parser with instance methods.
998 function compileAllStubs(typesOffset) {
999 var funs;
1000 var fun = compile(name, descriptor[0]);
1001 fun[#callName] = descriptor[1];
1002 holder[name] = fun;
1003 funs = [fun];
1004 // We iterate in blocks of 3 but have to stop before we reach the
1005 // (optional) two trailing items. To accomplish this, we only iterate
1006 // until we reach length - 2.
1007 for (var pos = 4; pos < descriptor.length - 2; pos += 3) {
1008 var stubName = descriptor[pos];
1009 fun = compile(stubName, descriptor[pos + 2]);
1010 fun[#callName] = descriptor[pos + 1];
1011 holder[stubName] = fun;
1012 funs.push(fun);
1013 }
1014 if (descriptor[2] != null) { // tear-off name.
1015 // functions, reflectionInfo, isStatic, name, isIntercepted.
1016 var reflectionInfo = descriptor[3];
1017 if (typeof reflectionInfo == "number") {
1018 reflectionInfo = reflectionInfo + typesOffset;
1019 }
1020 holder[descriptor[2]] =
1021 tearOff(funs, reflectionInfo, true, name, false);
1022 }
1023 if (pos < descriptor.length) {
1024 fun[#argumentCount] = descriptor[pos];
1025 fun[#defaultArgumentValues] = descriptor[pos + 1];
1026 }
1027 }
1028
1029 function setupCompileAllAndDelegateStub(name, typesOffset) {
1030 holder[name] = function() {
1031 // The descriptor is null if we already compiled this function. This
1032 // happens when we have calls to the static as arguments to the
1033 // static: `foo(foo(499))`;
1034 if (descriptor != null) {
1035 compileAllStubs(typesOffset);
1036 descriptor = null; // GC the descriptor.
1037 }
1038 return holder[name].apply(this, arguments);
1039 };
1040 }
1041
1042 setupCompileAllAndDelegateStub(name, typesOffset);
1043 for (var pos = 4; pos < descriptor.length; pos += 3) {
1044 setupCompileAllAndDelegateStub(descriptor[pos], typesOffset);
1045 }
1046 if (descriptor[2] != null) { // tear-off name.
1047 setupCompileAllAndDelegateStub(descriptor[2], typesOffset)
1048 }
1049 }
1050 }
1051
1052 function setupLazyStatic(name, getterName, holder, descriptor) {
1053 holder[name] = null;
1054 holder[getterName] = function() {
1055 var initializer = compile(name, descriptor);
1056 holder[getterName] = function() { #cyclicThrow(name) };
1057 var result;
1058 var sentinelInProgress = descriptor;
1059 try {
1060 result = holder[name] = sentinelInProgress;
1061 result = holder[name] = initializer();
1062 } finally {
1063 // Use try-finally, not try-catch/throw as it destroys the stack trace.
1064 if (result === sentinelInProgress) {
1065 // The lazy static (holder[name]) might have been set to a different
1066 // value. According to spec we still have to reset it to null, if the
1067 // initialization failed.
1068 holder[name] = null;
1069 }
1070 // TODO(floitsch): the function should probably be unique for each
1071 // static.
1072 holder[getterName] = function() { return this[name]; };
1073 }
1074 return result;
1075 };
1076 }
1077
1078 function setupConstant(name, holder, descriptor) {
1079 var c;
1080 holder[name] = function() {
1081 if (descriptor !== null) {
1082 c = compile(name, descriptor);
1083 name = null;
1084 descriptor = null;
1085 }
1086 return c;
1087 };
1088 }
1089
1090 function setupClass(name, holder, descriptor, typesOffset) {
1091 var patch = function() {
1092 if (patch.ensureResolved == patch) {
1093 // We have not yet been compiled.
1094 var constructor = compileConstructor(name, descriptor, typesOffset);
1095 holder[name] = constructor;
1096 name = holder = descriptor = null; // GC the captured arguments.
1097 // Make sure we can invoke 'ensureResolved' multiple times on the patch
1098 // function.
1099 patch.ensureResolved = function() { return constructor; };
1100 constructor.ensureResolved = function() { return this; };
1101 } else {
1102 // This can happen when arguments to the constructor are of the same
1103 // class, like in `new A(new A(null))`.
1104 constructor = patch.ensureResolved();
1105 }
1106 // If the patch has been called as "ensureResolved" return.
1107 if (this === patch) return constructor;
1108 var object = new constructor();
1109 constructor.apply(object, arguments);
1110 return object;
1111 };
1112
1113 // We store the patch function on itself to make it
1114 // possible to resolve superclass references without constructing instances.
1115 patch.ensureResolved = patch;
1116 holder[name] = patch;
1117 }
1118
1119 #tearOff;
1120
1121 #parseFunctionDescriptor;
1122
1123 function compileConstructor(name, descriptor, typesOffset) {
1124 descriptor = compile(name, descriptor);
1125 var prototype = determinePrototype(descriptor);
1126 var constructor;
1127 var functionsIndex;
1128 // $mixinFormatDescription.
1129 if (typeof descriptor[2] !== 'function') {
1130 fillPrototypeWithMixedIn(descriptor[2], descriptor[3], prototype);
1131 // descriptor[4] contains the constructor if the mixin application is
1132 // directly instantiated.
1133 if (typeof descriptor[4] === 'function') {
1134 constructor = descriptor[4];
1135 functionsIndex = 5;
1136 } else {
1137 constructor = function() {};
1138 functionsIndex = 4;
1139 }
1140 } else {
1141 constructor = descriptor[2];
1142 functionsIndex = 3;
1143 }
1144
1145 for (var i = functionsIndex; i < descriptor.length; i += 2) {
1146 parseFunctionDescriptor(prototype, descriptor[i], descriptor[i + 1],
1147 typesOffset);
1148 }
1149
1150 constructor.$typeNameProperty = name; // Needed for RTI.
1151 constructor.prototype = prototype;
1152 prototype[#operatorIsPrefix + name] = constructor;
1153 prototype.constructor = constructor;
1154 return constructor;
1155 }
1156
1157 function fillPrototypeWithMixedIn(mixinName, mixinHolderIndex, prototype) {
1158 var mixin = holders[mixinHolderIndex][mixinName].ensureResolved();
1159 var mixinPrototype = mixin.prototype;
1160
1161 // Fill the prototype with the mixin's properties.
1162 var mixinProperties = Object.keys(mixinPrototype);
1163 for (var i = 0; i < mixinProperties.length; i++) {
1164 var p = mixinProperties[i];
1165 prototype[p] = mixinPrototype[p];
1166 }
1167 }
1168
1169 function determinePrototype(descriptor) {
1170 var superclassName = descriptor[0];
1171 if (!superclassName) return { };
1172
1173 // Look up the superclass constructor function in the right holder.
1174 var holderIndex = descriptor[1];
1175 var superclass = holders[holderIndex][superclassName].ensureResolved();
1176
1177 // Create a new prototype object chained to the superclass prototype.
1178 var intermediate = function() { };
1179 intermediate.prototype = superclass.prototype;
1180 return new intermediate();
1181 }
1182
1183 function compile(__name__, __s__) {
1184 'use strict';
1185 // TODO(floitsch): evaluate the performance impact of the string
1186 // concatenations.
1187 return eval(__s__ + "\\n//# sourceURL=" + __name__ + ".js");
1188 }
1189
1190 if (#outputContainsConstantList) {
1191 function makeConstList(list) {
1192 // By assigning a function to the properties they become part of the
1193 // hidden class. The actual values of the fields don't matter, since we
1194 // only check if they exist.
1195 list.immutable\$list = Array;
1196 list.fixed\$length = Array;
1197 return list;
1198 }
1199 }
1200
1201 if (#needsNativeSupport) {
1202 function handleNativeClassInfos() {
1203 for (var nativeClass in nativeInfos) {
1204 var constructor = holdersMap[nativeClass][nativeClass].ensureResolved();
1205 var nativeInfo = nativeInfos[nativeClass];
1206 #nativeInfoHandler;
1207 }
1208 }
1209 }
1210
1211 $setupProgramName(program, 0);
1212
1213 // Initialize globals.
1214 #embeddedGlobals;
1215
1216 function expressionCompile(__s__) {
1217 'use strict';
1218 return eval('(' + __s__ + ')');
1219 }
1220
1221 #readMetadataTypeFunction;
1222
1223 // TODO(floitsch): this order means that native classes may not be
1224 // referenced from constants. I'm mostly afraid of things like using them as
1225 // generic arguments (which should be fine, but maybe there are other
1226 // similar things).
1227 // Initialize natives.
1228 if (#needsNativeSupport) handleNativeClassInfos();
1229
1230 // Initialize static non-final fields.
1231 #staticNonFinals;
1232
1233 // Add native boilerplate code.
1234 #nativeIsolateAffinityTagInitialization;
1235
1236 // Initialize eager classes.
1237 #eagerClasses;
1238
1239 var end = Date.now();
1240 // print('Setup: ' + (end - start) + ' ms.');
1241
1242 #invokeMain; // Start main.
1243
1244 })(Date.now(), #code)
1245 }""";
1246
1247 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698