OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js.js_emitter.lazy_emitter.model_emitter; | 5 library dart2js.js_emitter.lazy_emitter.model_emitter; |
6 | 6 |
7 import '../../compiler.dart' show | 7 import '../../compiler.dart' show Compiler; |
8 Compiler; | 8 import '../../constants/values.dart' show ConstantValue, FunctionConstantValue; |
9 import '../../constants/values.dart' show | 9 import '../../core_types.dart' show CoreClasses; |
10 ConstantValue, | 10 import '../../elements/elements.dart' show ClassElement, FunctionElement; |
11 FunctionConstantValue; | |
12 import '../../core_types.dart' show | |
13 CoreClasses; | |
14 import '../../elements/elements.dart' show | |
15 ClassElement, | |
16 FunctionElement; | |
17 import '../../js/js.dart' as js; | 11 import '../../js/js.dart' as js; |
18 import '../../js_backend/js_backend.dart' show | 12 import '../../js_backend/js_backend.dart' |
19 JavaScriptBackend, | 13 show JavaScriptBackend, Namer, ConstantEmitter; |
20 Namer, | |
21 ConstantEmitter; | |
22 | 14 |
23 import '../js_emitter.dart' show NativeEmitter; | 15 import '../js_emitter.dart' show NativeEmitter; |
24 import '../constant_ordering.dart' show deepCompareConstants; | 16 import '../constant_ordering.dart' show deepCompareConstants; |
25 | 17 |
26 import 'package:js_runtime/shared/embedded_names.dart' show | 18 import 'package:js_runtime/shared/embedded_names.dart' |
27 CREATE_NEW_ISOLATE, | 19 show |
28 DEFERRED_LIBRARY_URIS, | 20 CREATE_NEW_ISOLATE, |
29 DEFERRED_LIBRARY_HASHES, | 21 DEFERRED_LIBRARY_URIS, |
30 GET_TYPE_FROM_NAME, | 22 DEFERRED_LIBRARY_HASHES, |
31 INITIALIZE_LOADED_HUNK, | 23 GET_TYPE_FROM_NAME, |
32 INTERCEPTORS_BY_TAG, | 24 INITIALIZE_LOADED_HUNK, |
33 IS_HUNK_INITIALIZED, | 25 INTERCEPTORS_BY_TAG, |
34 IS_HUNK_LOADED, | 26 IS_HUNK_INITIALIZED, |
35 LEAF_TAGS, | 27 IS_HUNK_LOADED, |
36 MANGLED_GLOBAL_NAMES, | 28 LEAF_TAGS, |
37 METADATA, | 29 MANGLED_GLOBAL_NAMES, |
38 TYPE_TO_INTERCEPTOR_MAP, | 30 METADATA, |
39 TYPES; | 31 TYPE_TO_INTERCEPTOR_MAP, |
| 32 TYPES; |
40 | 33 |
41 import '../js_emitter.dart' show NativeGenerator, buildTearOffCode; | 34 import '../js_emitter.dart' show NativeGenerator, buildTearOffCode; |
42 import '../model.dart'; | 35 import '../model.dart'; |
43 | 36 |
44 class ModelEmitter { | 37 class ModelEmitter { |
45 final Compiler compiler; | 38 final Compiler compiler; |
46 final Namer namer; | 39 final Namer namer; |
47 ConstantEmitter constantEmitter; | 40 ConstantEmitter constantEmitter; |
48 final NativeEmitter nativeEmitter; | 41 final NativeEmitter nativeEmitter; |
49 | 42 |
50 JavaScriptBackend get backend => compiler.backend; | 43 JavaScriptBackend get backend => compiler.backend; |
51 | 44 |
52 /// For deferred loading we communicate the initializers via this global var. | 45 /// For deferred loading we communicate the initializers via this global var. |
53 static const String deferredInitializersGlobal = | 46 static const String deferredInitializersGlobal = |
54 r"$__dart_deferred_initializers__"; | 47 r"$__dart_deferred_initializers__"; |
55 | 48 |
56 static const String deferredExtension = "part.js"; | 49 static const String deferredExtension = "part.js"; |
57 | 50 |
58 static const String typeNameProperty = r"builtin$cls"; | 51 static const String typeNameProperty = r"builtin$cls"; |
59 | 52 |
60 ModelEmitter(Compiler compiler, Namer namer, this.nativeEmitter) | 53 ModelEmitter(Compiler compiler, Namer namer, this.nativeEmitter) |
61 : this.compiler = compiler, | 54 : this.compiler = compiler, |
62 this.namer = namer { | 55 this.namer = namer { |
63 | |
64 this.constantEmitter = new ConstantEmitter( | 56 this.constantEmitter = new ConstantEmitter( |
65 compiler, namer, this.generateConstantReference, | 57 compiler, namer, this.generateConstantReference, constantListGenerator); |
66 constantListGenerator); | |
67 } | 58 } |
68 | 59 |
69 js.Expression constantListGenerator(js.Expression array) { | 60 js.Expression constantListGenerator(js.Expression array) { |
70 // TODO(floitsch): remove hard-coded name. | 61 // TODO(floitsch): remove hard-coded name. |
71 return js.js('makeConstList(#)', [array]); | 62 return js.js('makeConstList(#)', [array]); |
72 } | 63 } |
73 | 64 |
74 js.Expression generateEmbeddedGlobalAccess(String global) { | 65 js.Expression generateEmbeddedGlobalAccess(String global) { |
75 return js.js(generateEmbeddedGlobalAccessString(global)); | 66 return js.js(generateEmbeddedGlobalAccessString(global)); |
76 } | 67 } |
77 | 68 |
78 String generateEmbeddedGlobalAccessString(String global) { | 69 String generateEmbeddedGlobalAccessString(String global) { |
79 // TODO(floitsch): don't use 'init' as global embedder storage. | 70 // TODO(floitsch): don't use 'init' as global embedder storage. |
80 return 'init.$global'; | 71 return 'init.$global'; |
81 } | 72 } |
82 | 73 |
83 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) { | 74 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) { |
84 if (constant.isFunction) return true; // Already emitted. | 75 if (constant.isFunction) return true; // Already emitted. |
85 if (constant.isPrimitive) return true; // Inlined. | 76 if (constant.isPrimitive) return true; // Inlined. |
86 if (constant.isDummy) return true; // Inlined. | 77 if (constant.isDummy) return true; // Inlined. |
87 // The name is null when the constant is already a JS constant. | 78 // The name is null when the constant is already a JS constant. |
88 // TODO(floitsch): every constant should be registered, so that we can | 79 // TODO(floitsch): every constant should be registered, so that we can |
89 // share the ones that take up too much space (like some strings). | 80 // share the ones that take up too much space (like some strings). |
90 if (namer.constantName(constant) == null) return true; | 81 if (namer.constantName(constant) == null) return true; |
91 return false; | 82 return false; |
92 } | 83 } |
93 | 84 |
94 // TODO(floitsch): copied from full emitter. Adjust or share. | 85 // TODO(floitsch): copied from full emitter. Adjust or share. |
95 int compareConstants(ConstantValue a, ConstantValue b) { | 86 int compareConstants(ConstantValue a, ConstantValue b) { |
96 // Inlined constants don't affect the order and sometimes don't even have | 87 // Inlined constants don't affect the order and sometimes don't even have |
(...skipping 26 matching lines...) Expand all Loading... |
123 if (value.isFunction) { | 114 if (value.isFunction) { |
124 FunctionConstantValue functionConstant = value; | 115 FunctionConstantValue functionConstant = value; |
125 return generateStaticClosureAccess(functionConstant.element); | 116 return generateStaticClosureAccess(functionConstant.element); |
126 } | 117 } |
127 | 118 |
128 // We are only interested in the "isInlined" part, but it does not hurt to | 119 // We are only interested in the "isInlined" part, but it does not hurt to |
129 // test for the other predicates. | 120 // test for the other predicates. |
130 if (isConstantInlinedOrAlreadyEmitted(value)) { | 121 if (isConstantInlinedOrAlreadyEmitted(value)) { |
131 return constantEmitter.generate(value); | 122 return constantEmitter.generate(value); |
132 } | 123 } |
133 return js.js('#.#()', [namer.globalObjectForConstant(value), | 124 return js.js('#.#()', |
134 namer.constantName(value)]); | 125 [namer.globalObjectForConstant(value), namer.constantName(value)]); |
135 } | 126 } |
136 | 127 |
137 int emitProgram(Program program) { | 128 int emitProgram(Program program) { |
138 List<Fragment> fragments = program.fragments; | 129 List<Fragment> fragments = program.fragments; |
139 MainFragment mainFragment = fragments.first; | 130 MainFragment mainFragment = fragments.first; |
140 Iterable<Fragment> deferredFragments = program.deferredFragments; | 131 Iterable<Fragment> deferredFragments = program.deferredFragments; |
141 | 132 |
142 int totalSize = 0; | 133 int totalSize = 0; |
143 | 134 |
144 // We have to emit the deferred fragments first, since we need their | 135 // We have to emit the deferred fragments first, since we need their |
145 // deferred hash (which depends on the output) when emitting the main | 136 // deferred hash (which depends on the output) when emitting the main |
146 // fragment. | 137 // fragment. |
147 List<js.Expression> fragmentsCode = deferredFragments.map( | 138 List<js.Expression> fragmentsCode = |
148 (DeferredFragment deferredUnit) { | 139 deferredFragments.map((DeferredFragment deferredUnit) { |
149 js.Expression types = | 140 js.Expression types = |
150 program.metadataTypesForOutputUnit(deferredUnit.outputUnit); | 141 program.metadataTypesForOutputUnit(deferredUnit.outputUnit); |
151 return emitDeferredFragment(types, deferredUnit, program.holders); | 142 return emitDeferredFragment(types, deferredUnit, program.holders); |
152 }).toList(); | 143 }).toList(); |
153 | 144 |
154 js.Statement mainAst = emitMainFragment(program); | 145 js.Statement mainAst = emitMainFragment(program); |
155 | 146 |
156 js.TokenCounter counter = new js.TokenCounter(); | 147 js.TokenCounter counter = new js.TokenCounter(); |
157 fragmentsCode.forEach(counter.countTokens); | 148 fragmentsCode.forEach(counter.countTokens); |
158 counter.countTokens(mainAst); | 149 counter.countTokens(mainAst); |
159 | 150 |
160 program.finalizers.forEach((js.TokenFinalizer f) => f.finalizeTokens()); | 151 program.finalizers.forEach((js.TokenFinalizer f) => f.finalizeTokens()); |
161 | 152 |
162 // TODO(johnnniwinther): Support source maps in this emitter. | 153 // TODO(johnnniwinther): Support source maps in this emitter. |
163 for (int i = 0; i < fragmentsCode.length; ++i) { | 154 for (int i = 0; i < fragmentsCode.length; ++i) { |
164 String code = js.createCodeBuffer(fragmentsCode[i], compiler).getText(); | 155 String code = js.createCodeBuffer(fragmentsCode[i], compiler).getText(); |
165 totalSize += code.length; | 156 totalSize += code.length; |
166 compiler.outputProvider(fragments[i+1].outputFileName, deferredExtension) | 157 compiler.outputProvider( |
| 158 fragments[i + 1].outputFileName, deferredExtension) |
167 ..add(code) | 159 ..add(code) |
168 ..close(); | 160 ..close(); |
169 } | 161 } |
170 | 162 |
171 String mainCode = js.createCodeBuffer(mainAst, compiler).getText(); | 163 String mainCode = js.createCodeBuffer(mainAst, compiler).getText(); |
172 compiler.outputProvider(mainFragment.outputFileName, 'js') | 164 compiler.outputProvider(mainFragment.outputFileName, 'js') |
173 ..add(buildGeneratedBy(compiler)) | 165 ..add(buildGeneratedBy(compiler)) |
174 ..add(mainCode) | 166 ..add(mainCode) |
175 ..close(); | 167 ..close(); |
176 totalSize += mainCode.length; | 168 totalSize += mainCode.length; |
177 | 169 |
178 return totalSize; | 170 return totalSize; |
179 } | 171 } |
180 | 172 |
181 /// Returns a [js.Literal] that represents the string result of unparsing | 173 /// Returns a [js.Literal] that represents the string result of unparsing |
182 /// [value]. | 174 /// [value]. |
183 /// | 175 /// |
184 /// The returned node will, when its string value is requested, pretty-print | 176 /// The returned node will, when its string value is requested, pretty-print |
185 /// the given [value] and, if [protectForEval] is true, wrap the resulting | 177 /// the given [value] and, if [protectForEval] is true, wrap the resulting |
186 /// string in parenthesis. The result is also escaped. | 178 /// string in parenthesis. The result is also escaped. |
187 /// | 179 /// |
188 /// See [_UnparsedNode] for details. | 180 /// See [_UnparsedNode] for details. |
189 js.Literal unparse(Compiler compiler, js.Node value, | 181 js.Literal unparse(Compiler compiler, js.Node value, |
190 {bool protectForEval: true}) { | 182 {bool protectForEval: true}) { |
191 return new js.UnparsedNode(value, compiler, protectForEval); | 183 return new js.UnparsedNode(value, compiler, protectForEval); |
192 } | 184 } |
193 | 185 |
194 String buildGeneratedBy(compiler) { | 186 String buildGeneratedBy(compiler) { |
195 var suffix = ''; | 187 var suffix = ''; |
196 if (compiler.options.hasBuildId) { | 188 if (compiler.options.hasBuildId) { |
197 suffix = ' version: ${compiler.options.buildId}'; | 189 suffix = ' version: ${compiler.options.buildId}'; |
198 } | 190 } |
199 return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n'; | 191 return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n'; |
200 } | 192 } |
201 | 193 |
202 js.Statement emitMainFragment(Program program) { | 194 js.Statement emitMainFragment(Program program) { |
203 MainFragment fragment = program.fragments.first; | 195 MainFragment fragment = program.fragments.first; |
204 List<js.Expression> elements = fragment.libraries.map(emitLibrary).toList(); | 196 List<js.Expression> elements = fragment.libraries.map(emitLibrary).toList(); |
205 elements.add( | 197 elements.add( |
206 emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields)); | 198 emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields)); |
207 elements.add(emitConstants(fragment.constants)); | 199 elements.add(emitConstants(fragment.constants)); |
208 | 200 |
209 js.Expression code = new js.ArrayInitializer(elements); | 201 js.Expression code = new js.ArrayInitializer(elements); |
210 | 202 |
211 Map<String, dynamic> holes = | 203 Map<String, dynamic> holes = { |
212 {'deferredInitializer': emitDeferredInitializerGlobal(program.loadMap), | 204 'deferredInitializer': emitDeferredInitializerGlobal(program.loadMap), |
213 'holders': emitHolders(program.holders), | 205 'holders': emitHolders(program.holders), |
214 'tearOff': buildTearOffCode(backend), | 206 'tearOff': buildTearOffCode(backend), |
215 'parseFunctionDescriptor': | 207 'parseFunctionDescriptor': |
216 js.js.statement(parseFunctionDescriptorBoilerplate, | 208 js.js.statement(parseFunctionDescriptorBoilerplate, { |
217 {'argumentCount': js.string(namer.requiredParameterField), | 209 'argumentCount': js.string(namer.requiredParameterField), |
218 'defaultArgumentValues': js.string(namer.defaultValuesField), | 210 'defaultArgumentValues': js.string(namer.defaultValuesField), |
219 'callName': js.string(namer.callNameField)}), | 211 'callName': js.string(namer.callNameField) |
220 | 212 }), |
221 'cyclicThrow': | 213 'cyclicThrow': backend.emitter |
222 backend.emitter.staticFunctionAccess( | 214 .staticFunctionAccess(backend.helpers.cyclicThrowHelper), |
223 backend.helpers.cyclicThrowHelper), | 215 'outputContainsConstantList': program.outputContainsConstantList, |
224 'outputContainsConstantList': program.outputContainsConstantList, | 216 'embeddedGlobals': emitEmbeddedGlobals(program), |
225 'embeddedGlobals': emitEmbeddedGlobals(program), | 217 'readMetadataTypeFunction': readMetadataTypeFunction, |
226 'readMetadataTypeFunction': readMetadataTypeFunction, | 218 'staticNonFinals': |
227 'staticNonFinals': | 219 emitStaticNonFinalFields(fragment.staticNonFinalFields), |
228 emitStaticNonFinalFields(fragment.staticNonFinalFields), | 220 'operatorIsPrefix': js.string(namer.operatorIsPrefix), |
229 'operatorIsPrefix': js.string(namer.operatorIsPrefix), | 221 'callName': js.string(namer.callNameField), |
230 'callName': js.string(namer.callNameField), | 222 'argumentCount': js.string(namer.requiredParameterField), |
231 'argumentCount': js.string(namer.requiredParameterField), | 223 'defaultArgumentValues': js.string(namer.defaultValuesField), |
232 'defaultArgumentValues': js.string(namer.defaultValuesField), | 224 'eagerClasses': emitEagerClassInitializations(fragment.libraries), |
233 'eagerClasses': emitEagerClassInitializations(fragment.libraries), | 225 'invokeMain': fragment.invokeMain, |
234 'invokeMain': fragment.invokeMain, | 226 'code': code |
235 'code': code}; | 227 }; |
236 | 228 |
237 holes.addAll(nativeHoles(program)); | 229 holes.addAll(nativeHoles(program)); |
238 | 230 |
239 return js.js.statement(boilerplate, holes); | 231 return js.js.statement(boilerplate, holes); |
240 } | 232 } |
241 | 233 |
242 Map<String, dynamic> nativeHoles(Program program) { | 234 Map<String, dynamic> nativeHoles(Program program) { |
243 Map<String, dynamic> nativeHoles = <String, dynamic>{}; | 235 Map<String, dynamic> nativeHoles = <String, dynamic>{}; |
244 | 236 |
245 js.Statement nativeIsolateAffinityTagInitialization; | 237 js.Statement nativeIsolateAffinityTagInitialization; |
246 if (NativeGenerator.needsIsolateAffinityTagInitialization(backend)) { | 238 if (NativeGenerator.needsIsolateAffinityTagInitialization(backend)) { |
247 nativeIsolateAffinityTagInitialization = | 239 nativeIsolateAffinityTagInitialization = |
248 NativeGenerator.generateIsolateAffinityTagInitialization( | 240 NativeGenerator.generateIsolateAffinityTagInitialization( |
249 backend, | 241 backend, |
250 generateEmbeddedGlobalAccess, | 242 generateEmbeddedGlobalAccess, |
251 // TODO(floitsch): internStringFunction. | 243 // TODO(floitsch): internStringFunction. |
252 js.js("(function(x) { return x; })", [])); | 244 js.js("(function(x) { return x; })", [])); |
253 } else { | 245 } else { |
254 nativeIsolateAffinityTagInitialization = js.js.statement(";"); | 246 nativeIsolateAffinityTagInitialization = js.js.statement(";"); |
255 } | 247 } |
256 nativeHoles['nativeIsolateAffinityTagInitialization'] = | 248 nativeHoles['nativeIsolateAffinityTagInitialization'] = |
257 nativeIsolateAffinityTagInitialization; | 249 nativeIsolateAffinityTagInitialization; |
258 | 250 |
259 | |
260 js.Expression nativeInfoAccess = js.js('nativeInfo', []); | 251 js.Expression nativeInfoAccess = js.js('nativeInfo', []); |
261 js.Expression constructorAccess = js.js('constructor', []); | 252 js.Expression constructorAccess = js.js('constructor', []); |
262 Function subclassReadGenerator = (js.Expression subclass) { | 253 Function subclassReadGenerator = (js.Expression subclass) { |
263 return js.js('holdersMap[#][#].ensureResolved()', [subclass, subclass]); | 254 return js.js('holdersMap[#][#].ensureResolved()', [subclass, subclass]); |
264 }; | 255 }; |
265 js.Expression interceptorsByTagAccess = | 256 js.Expression interceptorsByTagAccess = |
266 generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG); | 257 generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG); |
267 js.Expression leafTagsAccess = | 258 js.Expression leafTagsAccess = generateEmbeddedGlobalAccess(LEAF_TAGS); |
268 generateEmbeddedGlobalAccess(LEAF_TAGS); | |
269 js.Statement nativeInfoHandler = NativeGenerator.buildNativeInfoHandler( | 259 js.Statement nativeInfoHandler = NativeGenerator.buildNativeInfoHandler( |
270 nativeInfoAccess, | 260 nativeInfoAccess, |
271 constructorAccess, | 261 constructorAccess, |
272 subclassReadGenerator, | 262 subclassReadGenerator, |
273 interceptorsByTagAccess, | 263 interceptorsByTagAccess, |
274 leafTagsAccess); | 264 leafTagsAccess); |
275 | 265 |
276 nativeHoles['needsNativeSupport'] = program.needsNativeSupport; | 266 nativeHoles['needsNativeSupport'] = program.needsNativeSupport; |
277 nativeHoles['needsNoNativeSupport'] = !program.needsNativeSupport; | 267 nativeHoles['needsNoNativeSupport'] = !program.needsNativeSupport; |
278 nativeHoles['nativeInfoHandler'] = nativeInfoHandler; | 268 nativeHoles['nativeInfoHandler'] = nativeInfoHandler; |
279 | 269 |
280 return nativeHoles; | 270 return nativeHoles; |
281 } | 271 } |
282 | 272 |
283 js.Block emitHolders(List<Holder> holders) { | 273 js.Block emitHolders(List<Holder> holders) { |
284 // The top-level variables for holders must *not* be renamed by the | 274 // The top-level variables for holders must *not* be renamed by the |
285 // JavaScript pretty printer because a lot of code already uses the | 275 // JavaScript pretty printer because a lot of code already uses the |
286 // non-renamed names. The generated code looks like this: | 276 // non-renamed names. The generated code looks like this: |
287 // | 277 // |
288 // var H = {}, ..., G = {}; | 278 // var H = {}, ..., G = {}; |
289 // var holders = [ H, ..., G ]; | 279 // var holders = [ H, ..., G ]; |
290 // | 280 // |
291 // and it is inserted at the top of the top-level function expression | 281 // and it is inserted at the top of the top-level function expression |
292 // that covers the entire program. | 282 // that covers the entire program. |
293 | 283 |
294 List<js.Statement> statements = [ | 284 List<js.Statement> statements = [ |
295 new js.ExpressionStatement( | 285 new js.ExpressionStatement(new js.VariableDeclarationList(holders |
296 new js.VariableDeclarationList(holders.map((e) => | 286 .map((e) => new js.VariableInitialization( |
297 new js.VariableInitialization( | 287 new js.VariableDeclaration(e.name, allowRename: false), |
298 new js.VariableDeclaration(e.name, allowRename: false), | 288 new js.ObjectInitializer(const []))) |
299 new js.ObjectInitializer(const []))).toList())), | 289 .toList())), |
300 js.js.statement('var holders = #', new js.ArrayInitializer( | 290 js.js.statement( |
301 holders.map((e) => new js.VariableUse(e.name)) | 291 'var holders = #', |
302 .toList(growable: false))), | 292 new js.ArrayInitializer(holders |
303 js.js.statement('var holdersMap = Object.create(null)') | 293 .map((e) => new js.VariableUse(e.name)) |
| 294 .toList(growable: false))), |
| 295 js.js.statement('var holdersMap = Object.create(null)') |
304 ]; | 296 ]; |
305 return new js.Block(statements); | 297 return new js.Block(statements); |
306 } | 298 } |
307 | 299 |
308 js.Block emitEmbeddedGlobals(Program program) { | 300 js.Block emitEmbeddedGlobals(Program program) { |
309 List<js.Property> globals = <js.Property>[]; | 301 List<js.Property> globals = <js.Property>[]; |
310 | 302 |
311 if (program.loadMap.isNotEmpty) { | 303 if (program.loadMap.isNotEmpty) { |
312 globals.addAll(emitEmbeddedGlobalsForDeferredLoading(program.loadMap)); | 304 globals.addAll(emitEmbeddedGlobalsForDeferredLoading(program.loadMap)); |
313 } | 305 } |
314 | 306 |
315 if (program.typeToInterceptorMap != null) { | 307 if (program.typeToInterceptorMap != null) { |
316 globals.add(new js.Property(js.string(TYPE_TO_INTERCEPTOR_MAP), | 308 globals.add(new js.Property( |
317 program.typeToInterceptorMap)); | 309 js.string(TYPE_TO_INTERCEPTOR_MAP), program.typeToInterceptorMap)); |
318 } | 310 } |
319 | 311 |
320 if (program.hasIsolateSupport) { | 312 if (program.hasIsolateSupport) { |
321 String isolateName = namer.staticStateHolder; | 313 String isolateName = namer.staticStateHolder; |
322 globals.add( | 314 globals.add(new js.Property(js.string(CREATE_NEW_ISOLATE), |
323 new js.Property(js.string(CREATE_NEW_ISOLATE), | 315 js.js('function () { return $isolateName; }'))); |
324 js.js('function () { return $isolateName; }'))); | |
325 // TODO(floitsch): add remaining isolate functions. | 316 // TODO(floitsch): add remaining isolate functions. |
326 } | 317 } |
327 | 318 |
328 globals.add(emitMangledGlobalNames()); | 319 globals.add(emitMangledGlobalNames()); |
329 | 320 |
330 globals.add(emitGetTypeFromName()); | 321 globals.add(emitGetTypeFromName()); |
331 | 322 |
332 globals.addAll(emitMetadata(program)); | 323 globals.addAll(emitMetadata(program)); |
333 | 324 |
334 if (program.needsNativeSupport) { | 325 if (program.needsNativeSupport) { |
335 globals.add(new js.Property(js.string(INTERCEPTORS_BY_TAG), | 326 globals.add(new js.Property( |
336 js.js('Object.create(null)', []))); | 327 js.string(INTERCEPTORS_BY_TAG), js.js('Object.create(null)', []))); |
337 globals.add(new js.Property(js.string(LEAF_TAGS), | 328 globals.add(new js.Property( |
338 js.js('Object.create(null)', []))); | 329 js.string(LEAF_TAGS), js.js('Object.create(null)', []))); |
339 } | 330 } |
340 | 331 |
341 js.ObjectInitializer globalsObject = new js.ObjectInitializer(globals); | 332 js.ObjectInitializer globalsObject = new js.ObjectInitializer(globals); |
342 | 333 |
343 List<js.Statement> statements = | 334 List<js.Statement> statements = [ |
344 [new js.ExpressionStatement( | 335 new js.ExpressionStatement(new js.VariableDeclarationList([ |
345 new js.VariableDeclarationList( | 336 new js.VariableInitialization( |
346 [new js.VariableInitialization( | 337 new js.VariableDeclaration("init", allowRename: false), |
347 new js.VariableDeclaration("init", allowRename: false), | 338 globalsObject) |
348 globalsObject)]))]; | 339 ])) |
| 340 ]; |
349 return new js.Block(statements); | 341 return new js.Block(statements); |
350 } | 342 } |
351 | 343 |
352 js.Property emitMangledGlobalNames() { | 344 js.Property emitMangledGlobalNames() { |
353 List<js.Property> names = <js.Property>[]; | 345 List<js.Property> names = <js.Property>[]; |
354 | 346 |
355 CoreClasses coreClasses = compiler.coreClasses; | 347 CoreClasses coreClasses = compiler.coreClasses; |
356 // We want to keep the original names for the most common core classes when | 348 // We want to keep the original names for the most common core classes when |
357 // calling toString on them. | 349 // calling toString on them. |
358 List<ClassElement> nativeClassesNeedingUnmangledName = | 350 List<ClassElement> nativeClassesNeedingUnmangledName = [ |
359 [coreClasses.intClass, coreClasses.doubleClass, coreClasses.numClass, | 351 coreClasses.intClass, |
360 coreClasses.stringClass, coreClasses.boolClass, coreClasses.nullClass, | 352 coreClasses.doubleClass, |
361 coreClasses.listClass]; | 353 coreClasses.numClass, |
| 354 coreClasses.stringClass, |
| 355 coreClasses.boolClass, |
| 356 coreClasses.nullClass, |
| 357 coreClasses.listClass |
| 358 ]; |
362 nativeClassesNeedingUnmangledName.forEach((element) { | 359 nativeClassesNeedingUnmangledName.forEach((element) { |
363 names.add(new js.Property(js.quoteName(namer.className(element)), | 360 names.add(new js.Property( |
364 js.string(element.name))); | 361 js.quoteName(namer.className(element)), js.string(element.name))); |
365 }); | 362 }); |
366 | 363 |
367 return new js.Property(js.string(MANGLED_GLOBAL_NAMES), | 364 return new js.Property( |
368 new js.ObjectInitializer(names)); | 365 js.string(MANGLED_GLOBAL_NAMES), new js.ObjectInitializer(names)); |
369 } | 366 } |
370 | 367 |
371 js.Statement emitDeferredInitializerGlobal(Map loadMap) { | 368 js.Statement emitDeferredInitializerGlobal(Map loadMap) { |
372 if (loadMap.isEmpty) return new js.Block.empty(); | 369 if (loadMap.isEmpty) return new js.Block.empty(); |
373 | 370 |
374 return js.js.statement(""" | 371 return js.js.statement(""" |
375 if (typeof($deferredInitializersGlobal) === 'undefined') | 372 if (typeof($deferredInitializersGlobal) === 'undefined') |
376 var $deferredInitializersGlobal = Object.create(null);"""); | 373 var $deferredInitializersGlobal = Object.create(null);"""); |
377 } | 374 } |
378 | 375 |
379 Iterable<js.Property> emitEmbeddedGlobalsForDeferredLoading( | 376 Iterable<js.Property> emitEmbeddedGlobalsForDeferredLoading( |
380 Map<String, List<Fragment>> loadMap) { | 377 Map<String, List<Fragment>> loadMap) { |
381 | |
382 List<js.Property> globals = <js.Property>[]; | 378 List<js.Property> globals = <js.Property>[]; |
383 | 379 |
384 js.ArrayInitializer fragmentUris(List<Fragment> fragments) { | 380 js.ArrayInitializer fragmentUris(List<Fragment> fragments) { |
385 return js.stringArray(fragments.map((DeferredFragment fragment) => | 381 return js.stringArray(fragments.map((DeferredFragment fragment) => |
386 "${fragment.outputFileName}.$deferredExtension")); | 382 "${fragment.outputFileName}.$deferredExtension")); |
387 } | 383 } |
388 js.ArrayInitializer fragmentHashes(List<Fragment> fragments) { | 384 js.ArrayInitializer fragmentHashes(List<Fragment> fragments) { |
389 // TODO(floitsch): the hash must depend on the generated code. | 385 // TODO(floitsch): the hash must depend on the generated code. |
390 return js.numArray( | 386 return js.numArray( |
391 fragments.map((DeferredFragment fragment) => fragment.hashCode)); | 387 fragments.map((DeferredFragment fragment) => fragment.hashCode)); |
392 } | 388 } |
393 | 389 |
394 List<js.Property> uris = new List<js.Property>(loadMap.length); | 390 List<js.Property> uris = new List<js.Property>(loadMap.length); |
395 List<js.Property> hashes = new List<js.Property>(loadMap.length); | 391 List<js.Property> hashes = new List<js.Property>(loadMap.length); |
396 int count = 0; | 392 int count = 0; |
397 loadMap.forEach((String loadId, List<Fragment> fragmentList) { | 393 loadMap.forEach((String loadId, List<Fragment> fragmentList) { |
398 uris[count] = | 394 uris[count] = |
399 new js.Property(js.string(loadId), fragmentUris(fragmentList)); | 395 new js.Property(js.string(loadId), fragmentUris(fragmentList)); |
400 hashes[count] = | 396 hashes[count] = |
401 new js.Property(js.string(loadId), fragmentHashes(fragmentList)); | 397 new js.Property(js.string(loadId), fragmentHashes(fragmentList)); |
402 count++; | 398 count++; |
403 }); | 399 }); |
404 | 400 |
405 globals.add(new js.Property(js.string(DEFERRED_LIBRARY_URIS), | 401 globals.add(new js.Property( |
406 new js.ObjectInitializer(uris))); | 402 js.string(DEFERRED_LIBRARY_URIS), new js.ObjectInitializer(uris))); |
407 globals.add(new js.Property(js.string(DEFERRED_LIBRARY_HASHES), | 403 globals.add(new js.Property( |
408 new js.ObjectInitializer(hashes))); | 404 js.string(DEFERRED_LIBRARY_HASHES), new js.ObjectInitializer(hashes))); |
409 | 405 |
410 js.Expression isHunkLoadedFunction = | 406 js.Expression isHunkLoadedFunction = |
411 js.js("function(hash) { return !!$deferredInitializersGlobal[hash]; }"); | 407 js.js("function(hash) { return !!$deferredInitializersGlobal[hash]; }"); |
412 globals.add(new js.Property(js.string(IS_HUNK_LOADED), | 408 globals |
413 isHunkLoadedFunction)); | 409 .add(new js.Property(js.string(IS_HUNK_LOADED), isHunkLoadedFunction)); |
414 | 410 |
415 js.Expression isHunkInitializedFunction = | 411 js.Expression isHunkInitializedFunction = |
416 js.js("function(hash) { return false; }"); | 412 js.js("function(hash) { return false; }"); |
417 globals.add(new js.Property(js.string(IS_HUNK_INITIALIZED), | 413 globals.add(new js.Property( |
418 isHunkInitializedFunction)); | 414 js.string(IS_HUNK_INITIALIZED), isHunkInitializedFunction)); |
419 | 415 |
420 js.Expression typesAccess = generateEmbeddedGlobalAccess(TYPES); | 416 js.Expression typesAccess = generateEmbeddedGlobalAccess(TYPES); |
421 | 417 |
422 /// See [emitEmbeddedGlobalsForDeferredLoading] for the format of the | 418 /// See [emitEmbeddedGlobalsForDeferredLoading] for the format of the |
423 /// deferred hunk. | 419 /// deferred hunk. |
424 js.Expression initializeLoadedHunkFunction = | 420 js.Expression initializeLoadedHunkFunction = js.js( |
425 js.js(""" | 421 """ |
426 function(hash) { | 422 function(hash) { |
427 var hunk = $deferredInitializersGlobal[hash]; | 423 var hunk = $deferredInitializersGlobal[hash]; |
428 $setupProgramName(hunk[0], #typesAccess.length); | 424 $setupProgramName(hunk[0], #typesAccess.length); |
429 eval(hunk[1]); | 425 eval(hunk[1]); |
430 var deferredTypes = eval(hunk[2]); | 426 var deferredTypes = eval(hunk[2]); |
431 #typesAccess.push.apply(#typesAccess, deferredTypes); | 427 #typesAccess.push.apply(#typesAccess, deferredTypes); |
432 }""", {'typesAccess': typesAccess}); | 428 }""", |
| 429 {'typesAccess': typesAccess}); |
433 | 430 |
434 globals.add(new js.Property(js.string(INITIALIZE_LOADED_HUNK), | 431 globals.add(new js.Property( |
435 initializeLoadedHunkFunction)); | 432 js.string(INITIALIZE_LOADED_HUNK), initializeLoadedHunkFunction)); |
436 | 433 |
437 return globals; | 434 return globals; |
438 } | 435 } |
439 | 436 |
440 js.Property emitGetTypeFromName() { | 437 js.Property emitGetTypeFromName() { |
441 js.Expression function = | 438 js.Expression function = js.js("""function(name) { |
442 js.js( """function(name) { | |
443 return holdersMap[name][name].ensureResolved(); | 439 return holdersMap[name][name].ensureResolved(); |
444 }"""); | 440 }"""); |
445 return new js.Property(js.string(GET_TYPE_FROM_NAME), function); | 441 return new js.Property(js.string(GET_TYPE_FROM_NAME), function); |
446 } | 442 } |
447 | 443 |
448 static final String readMetadataTypeName = "readMetadataType"; | 444 static final String readMetadataTypeName = "readMetadataType"; |
449 | 445 |
450 js.Statement get readMetadataTypeFunction { | 446 js.Statement get readMetadataTypeFunction { |
451 // Types are non-evaluated and must be compiled at first use. | 447 // Types are non-evaluated and must be compiled at first use. |
452 // Compiled strings are guaranteed not to be strings, and it's thus safe | 448 // Compiled strings are guaranteed not to be strings, and it's thus safe |
453 // to use a type-test to determine if a type has already been compiled. | 449 // to use a type-test to determine if a type has already been compiled. |
454 return js.js.statement('''function $readMetadataTypeName(index) { | 450 return js.js.statement( |
| 451 '''function $readMetadataTypeName(index) { |
455 var type = #typesAccess[index]; | 452 var type = #typesAccess[index]; |
456 if (typeof type == 'string') { | 453 if (typeof type == 'string') { |
457 type = expressionCompile(type); | 454 type = expressionCompile(type); |
458 #typesAccess[index] = type; | 455 #typesAccess[index] = type; |
459 } | 456 } |
460 return type; | 457 return type; |
461 }''', {"typesAccess": generateEmbeddedGlobalAccess(TYPES)}); | 458 }''', |
| 459 {"typesAccess": generateEmbeddedGlobalAccess(TYPES)}); |
462 } | 460 } |
463 | 461 |
464 js.Template get templateForReadType { | 462 js.Template get templateForReadType { |
465 // TODO(floitsch): make sure that no local variable shadows the access to | 463 // TODO(floitsch): make sure that no local variable shadows the access to |
466 // the readMetadataType function. | 464 // the readMetadataType function. |
467 return js.js.expressionTemplateFor('$readMetadataTypeName(#)'); | 465 return js.js.expressionTemplateFor('$readMetadataTypeName(#)'); |
468 } | 466 } |
469 | 467 |
470 static final String readMetadataName = "readLazyMetadata"; | 468 static final String readMetadataName = "readLazyMetadata"; |
471 static final String lazyMetadataName = "lazy_$METADATA"; | 469 static final String lazyMetadataName = "lazy_$METADATA"; |
472 | 470 |
473 js.Statement get readMetadataFunction { | 471 js.Statement get readMetadataFunction { |
474 // Types are non-evaluated and must be compiled at first use. | 472 // Types are non-evaluated and must be compiled at first use. |
475 // Compiled strings are guaranteed not to be strings, and it's thus safe | 473 // Compiled strings are guaranteed not to be strings, and it's thus safe |
476 // to use a type-test to determine if a type has already been compiled. | 474 // to use a type-test to determine if a type has already been compiled. |
477 return js.js.statement('''function $readMetadataName(index) { | 475 return js.js.statement( |
| 476 '''function $readMetadataName(index) { |
478 var lazyMetadata = #lazyMetadataAccess[index]; | 477 var lazyMetadata = #lazyMetadataAccess[index]; |
479 if (typeof lazyMetadata == 'string') { | 478 if (typeof lazyMetadata == 'string') { |
480 #metadataAccess[index] = expressionCompile(lazyMetadata); | 479 #metadataAccess[index] = expressionCompile(lazyMetadata); |
481 #lazyMetadataAccess[index] = null; | 480 #lazyMetadataAccess[index] = null; |
482 } | 481 } |
483 return #metadataAccess[index]; | 482 return #metadataAccess[index]; |
484 }''', { | 483 }''', |
485 "lazyMetadataAccess": generateEmbeddedGlobalAccess(lazyMetadataName), | 484 { |
486 "metadataAccess": generateEmbeddedGlobalAccess(METADATA) | 485 "lazyMetadataAccess": generateEmbeddedGlobalAccess(lazyMetadataName), |
487 }); | 486 "metadataAccess": generateEmbeddedGlobalAccess(METADATA) |
| 487 }); |
488 } | 488 } |
489 | 489 |
490 js.Template get templateForReadMetadata { | 490 js.Template get templateForReadMetadata { |
491 // TODO(floitsch): make sure that no local variable shadows the access to | 491 // TODO(floitsch): make sure that no local variable shadows the access to |
492 // the readMetadata function. | 492 // the readMetadata function. |
493 return js.js.expressionTemplateFor('$readMetadataName(#)'); | 493 return js.js.expressionTemplateFor('$readMetadataName(#)'); |
494 } | 494 } |
495 | 495 |
496 List<js.Property> emitMetadata(Program program) { | 496 List<js.Property> emitMetadata(Program program) { |
497 List<js.Property> metadataGlobals = <js.Property>[]; | 497 List<js.Property> metadataGlobals = <js.Property>[]; |
498 | 498 |
499 js.Property createGlobal(js.Expression metadata, String global) { | 499 js.Property createGlobal(js.Expression metadata, String global) { |
500 return new js.Property(js.string(global), metadata); | 500 return new js.Property(js.string(global), metadata); |
501 } | 501 } |
502 | 502 |
503 metadataGlobals.add(createGlobal(program.metadata, METADATA)); | 503 metadataGlobals.add(createGlobal(program.metadata, METADATA)); |
504 js.Expression types = | 504 js.Expression types = |
505 program.metadataTypesForOutputUnit(program.mainFragment.outputUnit); | 505 program.metadataTypesForOutputUnit(program.mainFragment.outputUnit); |
506 metadataGlobals.add(createGlobal(types, TYPES)); | 506 metadataGlobals.add(createGlobal(types, TYPES)); |
507 | 507 |
508 return metadataGlobals; | 508 return metadataGlobals; |
509 } | 509 } |
510 | 510 |
511 js.Expression emitDeferredFragment(js.Expression deferredTypes, | 511 js.Expression emitDeferredFragment(js.Expression deferredTypes, |
512 DeferredFragment fragment, | 512 DeferredFragment fragment, List<Holder> holders) { |
513 List<Holder> holders) { | |
514 // TODO(floitsch): initialize eager classes. | 513 // TODO(floitsch): initialize eager classes. |
515 // TODO(floitsch): the hash must depend on the output. | 514 // TODO(floitsch): the hash must depend on the output. |
516 int hash = fragment.hashCode; | 515 int hash = fragment.hashCode; |
517 | 516 |
518 List<js.Expression> deferredCode = | 517 List<js.Expression> deferredCode = |
519 fragment.libraries.map(emitLibrary).toList(); | 518 fragment.libraries.map(emitLibrary).toList(); |
520 | 519 |
521 deferredCode.add( | 520 deferredCode.add( |
522 emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields)); | 521 emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields)); |
523 deferredCode.add(emitConstants(fragment.constants)); | 522 deferredCode.add(emitConstants(fragment.constants)); |
524 | 523 |
525 js.ArrayInitializer deferredArray = new js.ArrayInitializer(deferredCode); | 524 js.ArrayInitializer deferredArray = new js.ArrayInitializer(deferredCode); |
526 | 525 |
527 // This is the code that must be evaluated after all deferred classes have | 526 // This is the code that must be evaluated after all deferred classes have |
528 // been setup. | 527 // been setup. |
529 js.Statement immediateCode = new js.Block([ | 528 js.Statement immediateCode = new js.Block([ |
530 emitStaticNonFinalFields(fragment.staticNonFinalFields), | 529 emitStaticNonFinalFields(fragment.staticNonFinalFields), |
531 emitEagerClassInitializations(fragment.libraries)]); | 530 emitEagerClassInitializations(fragment.libraries) |
532 | 531 ]); |
533 | 532 |
534 js.Literal immediateString = unparse(compiler, immediateCode); | 533 js.Literal immediateString = unparse(compiler, immediateCode); |
535 | 534 |
536 js.ArrayInitializer hunk = | 535 js.ArrayInitializer hunk = new js.ArrayInitializer( |
537 new js.ArrayInitializer([deferredArray, immediateString, | 536 [deferredArray, immediateString, deferredTypes]); |
538 deferredTypes]); | |
539 | 537 |
540 return js.js("$deferredInitializersGlobal[$hash] = #", hunk); | 538 return js.js("$deferredInitializersGlobal[$hash] = #", hunk); |
541 } | 539 } |
542 | 540 |
543 // This string should be referenced wherever JavaScript code makes assumptions | 541 // This string should be referenced wherever JavaScript code makes assumptions |
544 // on the constants format. | 542 // on the constants format. |
545 static final String constantsDescription = | 543 static final String constantsDescription = |
546 "The constants are encoded as a follows:" | 544 "The constants are encoded as a follows:" |
547 " [constantsHolderIndex, name, code, name2, code2, ...]." | 545 " [constantsHolderIndex, name, code, name2, code2, ...]." |
548 "The array is completely empty if there is no constant at all."; | 546 "The array is completely empty if there is no constant at all."; |
549 | 547 |
550 js.ArrayInitializer emitConstants(List<Constant> constants) { | 548 js.ArrayInitializer emitConstants(List<Constant> constants) { |
551 List<js.Expression> data = <js.Expression>[]; | 549 List<js.Expression> data = <js.Expression>[]; |
552 if (constants.isNotEmpty) { | 550 if (constants.isNotEmpty) { |
553 int holderIndex = constants.first.holder.index; | 551 int holderIndex = constants.first.holder.index; |
554 data.add(js.number(holderIndex)); | 552 data.add(js.number(holderIndex)); |
555 data.addAll(constants.expand((Constant constant) { | 553 data.addAll(constants.expand((Constant constant) { |
556 assert(constant.holder.index == holderIndex); | 554 assert(constant.holder.index == holderIndex); |
557 js.Expression code = constantEmitter.generate(constant.value); | 555 js.Expression code = constantEmitter.generate(constant.value); |
558 return [js.quoteName(constant.name), unparse(compiler, code)]; | 556 return [js.quoteName(constant.name), unparse(compiler, code)]; |
559 })); | 557 })); |
560 } | 558 } |
561 return new js.ArrayInitializer(data); | 559 return new js.ArrayInitializer(data); |
562 } | 560 } |
563 | 561 |
564 js.Block emitStaticNonFinalFields(List<StaticField> fields) { | 562 js.Block emitStaticNonFinalFields(List<StaticField> fields) { |
565 Iterable<js.Statement> statements = fields.map((StaticField field) { | 563 Iterable<js.Statement> statements = fields.map((StaticField field) { |
566 return js.js.statement("#.# = #;", | 564 return js.js |
567 [field.holder.name, field.name, field.code]); | 565 .statement("#.# = #;", [field.holder.name, field.name, field.code]); |
568 }); | 566 }); |
569 return new js.Block(statements.toList()); | 567 return new js.Block(statements.toList()); |
570 } | 568 } |
571 | 569 |
572 js.Expression emitLazilyInitializedStatics(List<StaticField> fields) { | 570 js.Expression emitLazilyInitializedStatics(List<StaticField> fields) { |
573 Iterable fieldDescriptors = fields.expand((field) => | 571 Iterable fieldDescriptors = fields.expand((field) => [ |
574 [ js.quoteName(field.name), | 572 js.quoteName(field.name), |
575 js.quoteName(namer.deriveLazyInitializerName(field.name)), | 573 js.quoteName(namer.deriveLazyInitializerName(field.name)), |
576 js.number(field.holder.index), | 574 js.number(field.holder.index), |
577 emitLazyInitializer(field) ]); | 575 emitLazyInitializer(field) |
| 576 ]); |
578 return new js.ArrayInitializer(fieldDescriptors.toList(growable: false)); | 577 return new js.ArrayInitializer(fieldDescriptors.toList(growable: false)); |
579 } | 578 } |
580 | 579 |
581 js.Block emitEagerClassInitializations(List<Library> libraries) { | 580 js.Block emitEagerClassInitializations(List<Library> libraries) { |
582 js.Statement createInstantiation(Class cls) { | 581 js.Statement createInstantiation(Class cls) { |
583 return js.js.statement('new #.#()', [cls.holder.name, cls.name]); | 582 return js.js.statement('new #.#()', [cls.holder.name, cls.name]); |
584 } | 583 } |
585 | 584 |
586 List<js.Statement> instantiations = | 585 List<js.Statement> instantiations = libraries |
587 libraries.expand((Library library) => library.classes) | 586 .expand((Library library) => library.classes) |
588 .where((Class cls) => cls.isEager) | 587 .where((Class cls) => cls.isEager) |
589 .map(createInstantiation) | 588 .map(createInstantiation) |
590 .toList(growable: false); | 589 .toList(growable: false); |
591 return new js.Block(instantiations); | 590 return new js.Block(instantiations); |
592 } | 591 } |
593 | 592 |
594 // This string should be referenced wherever JavaScript code makes assumptions | 593 // This string should be referenced wherever JavaScript code makes assumptions |
595 // on the mixin format. | 594 // on the mixin format. |
596 static final String nativeInfoDescription = | 595 static final String nativeInfoDescription = "A class is encoded as follows:" |
597 "A class is encoded as follows:" | |
598 " [name, class-code, holder-index], or " | 596 " [name, class-code, holder-index], or " |
599 " [name, class-code, native-info, holder-index]."; | 597 " [name, class-code, native-info, holder-index]."; |
600 | 598 |
601 js.Expression emitLibrary(Library library) { | 599 js.Expression emitLibrary(Library library) { |
602 Iterable staticDescriptors = library.statics.expand(emitStaticMethod); | 600 Iterable staticDescriptors = library.statics.expand(emitStaticMethod); |
603 | 601 |
604 Iterable classDescriptors = library.classes.expand((Class cls) { | 602 Iterable classDescriptors = library.classes.expand((Class cls) { |
605 js.Literal name = js.quoteName(cls.name); | 603 js.Literal name = js.quoteName(cls.name); |
606 js.LiteralNumber holderIndex = js.number(cls.holder.index); | 604 js.LiteralNumber holderIndex = js.number(cls.holder.index); |
607 js.Expression emittedClass = emitClass(cls); | 605 js.Expression emittedClass = emitClass(cls); |
(...skipping 17 matching lines...) Expand all Loading... |
625 List<js.Name> fieldNames = const <js.Name>[]; | 623 List<js.Name> fieldNames = const <js.Name>[]; |
626 | 624 |
627 // If the class is not directly instantiated we only need it for inheritance | 625 // If the class is not directly instantiated we only need it for inheritance |
628 // or RTI. In either case we don't need its fields. | 626 // or RTI. In either case we don't need its fields. |
629 if (cls.isDirectlyInstantiated && !cls.isNative) { | 627 if (cls.isDirectlyInstantiated && !cls.isNative) { |
630 fieldNames = cls.fields.map((Field field) => field.name).toList(); | 628 fieldNames = cls.fields.map((Field field) => field.name).toList(); |
631 } | 629 } |
632 js.Name name = cls.name; | 630 js.Name name = cls.name; |
633 | 631 |
634 Iterable<js.Name> assignments = fieldNames.map((js.Name field) { | 632 Iterable<js.Name> assignments = fieldNames.map((js.Name field) { |
635 return js.js("this.#field = #field", {"field": field}); | 633 return js.js("this.#field = #field", {"field": field}); |
636 }); | 634 }); |
637 | 635 |
638 return js.js('function #(#) { # }', [name, fieldNames, assignments]); | 636 return js.js('function #(#) { # }', [name, fieldNames, assignments]); |
639 } | 637 } |
640 | 638 |
641 Method _generateGetter(Field field) { | 639 Method _generateGetter(Field field) { |
642 String getterTemplateFor(int flags) { | 640 String getterTemplateFor(int flags) { |
643 switch (flags) { | 641 switch (flags) { |
644 case 1: return "function() { return this[#]; }"; | 642 case 1: |
645 case 2: return "function(receiver) { return receiver[#]; }"; | 643 return "function() { return this[#]; }"; |
646 case 3: return "function(receiver) { return this[#]; }"; | 644 case 2: |
| 645 return "function(receiver) { return receiver[#]; }"; |
| 646 case 3: |
| 647 return "function(receiver) { return this[#]; }"; |
647 } | 648 } |
648 return null; | 649 return null; |
649 } | 650 } |
650 | 651 |
651 js.Expression fieldName = js.quoteName(field.name); | 652 js.Expression fieldName = js.quoteName(field.name); |
652 js.Expression code = js.js(getterTemplateFor(field.getterFlags), fieldName); | 653 js.Expression code = js.js(getterTemplateFor(field.getterFlags), fieldName); |
653 js.Name getterName = namer.deriveGetterName(field.accessorName); | 654 js.Name getterName = namer.deriveGetterName(field.accessorName); |
654 return new StubMethod(getterName, code); | 655 return new StubMethod(getterName, code); |
655 } | 656 } |
656 | 657 |
657 Method _generateSetter(Field field) { | 658 Method _generateSetter(Field field) { |
658 String setterTemplateFor(int flags) { | 659 String setterTemplateFor(int flags) { |
659 switch (flags) { | 660 switch (flags) { |
660 case 1: return "function(val) { return this[#] = val; }"; | 661 case 1: |
661 case 2: return "function(receiver, val) { return receiver[#] = val; }"; | 662 return "function(val) { return this[#] = val; }"; |
662 case 3: return "function(receiver, val) { return this[#] = val; }"; | 663 case 2: |
| 664 return "function(receiver, val) { return receiver[#] = val; }"; |
| 665 case 3: |
| 666 return "function(receiver, val) { return this[#] = val; }"; |
663 } | 667 } |
664 return null; | 668 return null; |
665 } | 669 } |
666 js.Expression fieldName = js.quoteName(field.name); | 670 js.Expression fieldName = js.quoteName(field.name); |
667 js.Expression code = js.js(setterTemplateFor(field.setterFlags), fieldName); | 671 js.Expression code = js.js(setterTemplateFor(field.setterFlags), fieldName); |
668 js.Name setterName = namer.deriveSetterName(field.accessorName); | 672 js.Name setterName = namer.deriveSetterName(field.accessorName); |
669 return new StubMethod(setterName, code); | 673 return new StubMethod(setterName, code); |
670 } | 674 } |
671 | 675 |
672 Iterable<Method> _generateGettersSetters(Class cls) { | 676 Iterable<Method> _generateGettersSetters(Class cls) { |
673 Iterable<Method> getters = cls.fields | 677 Iterable<Method> getters = cls.fields |
674 .where((Field field) => field.needsGetter) | 678 .where((Field field) => field.needsGetter) |
675 .map(_generateGetter); | 679 .map(_generateGetter); |
676 | 680 |
677 Iterable<Method> setters = cls.fields | 681 Iterable<Method> setters = cls.fields |
678 .where((Field field) => field.needsUncheckedSetter) | 682 .where((Field field) => field.needsUncheckedSetter) |
679 .map(_generateSetter); | 683 .map(_generateSetter); |
680 | 684 |
681 return [getters, setters].expand((x) => x); | 685 return [getters, setters].expand((x) => x); |
682 } | 686 } |
683 | 687 |
684 // This string should be referenced wherever JavaScript code makes assumptions | 688 // This string should be referenced wherever JavaScript code makes assumptions |
685 // on the mixin format. | 689 // on the mixin format. |
686 static final String mixinFormatDescription = | 690 static final String mixinFormatDescription = |
687 "Mixins have a reference to their mixin class at the place of the usual" | 691 "Mixins have a reference to their mixin class at the place of the usual" |
688 "constructor. If they are instantiated the constructor follows the" | 692 "constructor. If they are instantiated the constructor follows the" |
689 "reference."; | 693 "reference."; |
690 | 694 |
691 js.Expression emitClass(Class cls) { | 695 js.Expression emitClass(Class cls) { |
692 List elements = [js.quoteName(cls.superclassName, allowNull: true), | 696 List elements = [ |
693 js.number(cls.superclassHolderIndex)]; | 697 js.quoteName(cls.superclassName, allowNull: true), |
| 698 js.number(cls.superclassHolderIndex) |
| 699 ]; |
694 | 700 |
695 if (cls.isMixinApplication) { | 701 if (cls.isMixinApplication) { |
696 MixinApplication mixin = cls; | 702 MixinApplication mixin = cls; |
697 elements.add(js.quoteName(mixin.mixinClass.name)); | 703 elements.add(js.quoteName(mixin.mixinClass.name)); |
698 elements.add(js.number(mixin.mixinClass.holder.index)); | 704 elements.add(js.number(mixin.mixinClass.holder.index)); |
699 if (cls.isDirectlyInstantiated) { | 705 if (cls.isDirectlyInstantiated) { |
700 elements.add(_generateConstructor(cls)); | 706 elements.add(_generateConstructor(cls)); |
701 } | 707 } |
702 } else { | 708 } else { |
703 elements.add(_generateConstructor(cls)); | 709 elements.add(_generateConstructor(cls)); |
704 } | 710 } |
705 Iterable<Method> methods = cls.methods; | 711 Iterable<Method> methods = cls.methods; |
706 Iterable<Method> isChecks = cls.isChecks; | 712 Iterable<Method> isChecks = cls.isChecks; |
707 Iterable<Method> callStubs = cls.callStubs; | 713 Iterable<Method> callStubs = cls.callStubs; |
708 Iterable<Method> typeVariableReaderStubs = cls.typeVariableReaderStubs; | 714 Iterable<Method> typeVariableReaderStubs = cls.typeVariableReaderStubs; |
709 Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs; | 715 Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs; |
710 Iterable<Method> gettersSetters = _generateGettersSetters(cls); | 716 Iterable<Method> gettersSetters = _generateGettersSetters(cls); |
711 Iterable<Method> allMethods = | 717 Iterable<Method> allMethods = [ |
712 [methods, isChecks, callStubs, typeVariableReaderStubs, | 718 methods, |
713 noSuchMethodStubs, gettersSetters].expand((x) => x); | 719 isChecks, |
| 720 callStubs, |
| 721 typeVariableReaderStubs, |
| 722 noSuchMethodStubs, |
| 723 gettersSetters |
| 724 ].expand((x) => x); |
714 elements.addAll(allMethods.expand(emitInstanceMethod)); | 725 elements.addAll(allMethods.expand(emitInstanceMethod)); |
715 | 726 |
716 return unparse(compiler, new js.ArrayInitializer(elements)); | 727 return unparse(compiler, new js.ArrayInitializer(elements)); |
717 } | 728 } |
718 | 729 |
719 js.Expression emitLazyInitializer(StaticField field) { | 730 js.Expression emitLazyInitializer(StaticField field) { |
720 assert(field.isLazy); | 731 assert(field.isLazy); |
721 return unparse(compiler, field.code); | 732 return unparse(compiler, field.code); |
722 } | 733 } |
723 | 734 |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
806 if (method.optionalParameterDefaultValues is List) { | 817 if (method.optionalParameterDefaultValues is List) { |
807 List<ConstantValue> defaultValues = method.optionalParameterDefaultValues; | 818 List<ConstantValue> defaultValues = method.optionalParameterDefaultValues; |
808 Iterable<js.Expression> elements = | 819 Iterable<js.Expression> elements = |
809 defaultValues.map(generateConstantReference); | 820 defaultValues.map(generateConstantReference); |
810 return new js.ArrayInitializer(elements.toList()); | 821 return new js.ArrayInitializer(elements.toList()); |
811 } else { | 822 } else { |
812 Map<String, ConstantValue> defaultValues = | 823 Map<String, ConstantValue> defaultValues = |
813 method.optionalParameterDefaultValues; | 824 method.optionalParameterDefaultValues; |
814 List<js.Property> properties = <js.Property>[]; | 825 List<js.Property> properties = <js.Property>[]; |
815 defaultValues.forEach((String name, ConstantValue value) { | 826 defaultValues.forEach((String name, ConstantValue value) { |
816 properties.add(new js.Property(js.string(name), | 827 properties.add( |
817 generateConstantReference(value))); | 828 new js.Property(js.string(name), generateConstantReference(value))); |
818 }); | 829 }); |
819 return new js.ObjectInitializer(properties); | 830 return new js.ObjectInitializer(properties); |
820 } | 831 } |
821 } | 832 } |
822 | 833 |
823 Iterable<js.Expression> emitInstanceMethod(Method method) { | 834 Iterable<js.Expression> emitInstanceMethod(Method method) { |
824 | |
825 List<js.Expression> makeNameCodePair(Method method) { | 835 List<js.Expression> makeNameCodePair(Method method) { |
826 return [js.quoteName(method.name), method.code]; | 836 return [js.quoteName(method.name), method.code]; |
827 } | 837 } |
828 | 838 |
829 List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) { | 839 List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) { |
830 js.Expression callName = stub.callName == null | 840 js.Expression callName = stub.callName == null |
831 ? new js.LiteralNull() | 841 ? new js.LiteralNull() |
832 : js.quoteName(stub.callName); | 842 : js.quoteName(stub.callName); |
833 return [js.quoteName(stub.name), callName, stub.code]; | 843 return [js.quoteName(stub.name), callName, stub.code]; |
834 } | 844 } |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
898 // stub1_name, stub1_callName, stub1_code, ...] | 908 // stub1_name, stub1_callName, stub1_code, ...] |
899 var data = [unparse(compiler, method.code)]; | 909 var data = [unparse(compiler, method.code)]; |
900 data.add(js.quoteName(method.callName)); | 910 data.add(js.quoteName(method.callName)); |
901 data.add(js.quoteName(method.tearOffName)); | 911 data.add(js.quoteName(method.tearOffName)); |
902 data.add(method.functionType); | 912 data.add(method.functionType); |
903 data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet)); | 913 data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet)); |
904 if (method.canBeApplied) { | 914 if (method.canBeApplied) { |
905 data.add(js.number(method.requiredParameterCount)); | 915 data.add(js.number(method.requiredParameterCount)); |
906 data.add(_encodeOptionalParameterDefaultValues(method)); | 916 data.add(_encodeOptionalParameterDefaultValues(method)); |
907 } | 917 } |
908 return [js.quoteName(method.name), holderIndex, | 918 return [ |
909 new js.ArrayInitializer(data)]; | 919 js.quoteName(method.name), |
| 920 holderIndex, |
| 921 new js.ArrayInitializer(data) |
| 922 ]; |
910 } else { | 923 } else { |
911 method.parameterStubs.forEach(_addMethod); | 924 method.parameterStubs.forEach(_addMethod); |
912 } | 925 } |
913 } | 926 } |
914 return output; | 927 return output; |
915 } | 928 } |
916 | 929 |
917 static final String setupProgramName = "setupProgram"; | 930 static final String setupProgramName = "setupProgram"; |
918 | 931 |
919 static final String boilerplate = """ | 932 static final String boilerplate = """ |
(...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1250 // Initialize eager classes. | 1263 // Initialize eager classes. |
1251 #eagerClasses; | 1264 #eagerClasses; |
1252 | 1265 |
1253 var end = Date.now(); | 1266 var end = Date.now(); |
1254 // print('Setup: ' + (end - start) + ' ms.'); | 1267 // print('Setup: ' + (end - start) + ' ms.'); |
1255 | 1268 |
1256 #invokeMain; // Start main. | 1269 #invokeMain; // Start main. |
1257 | 1270 |
1258 })(Date.now(), #code) | 1271 })(Date.now(), #code) |
1259 }"""; | 1272 }"""; |
1260 | |
1261 } | 1273 } |
OLD | NEW |