OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 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 } | |
OLD | NEW |