| 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 |