| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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.startup_emitter.model_emitter; | 5 library dart2js.js_emitter.startup_emitter.model_emitter; |
| 6 | 6 |
| 7 import 'dart:convert' show JsonEncoder; |
| 8 |
| 9 import '../../common.dart'; |
| 10 |
| 7 import '../../constants/values.dart' show ConstantValue, FunctionConstantValue; | 11 import '../../constants/values.dart' show ConstantValue, FunctionConstantValue; |
| 8 import '../../dart2jslib.dart' show Compiler; | 12 import '../../dart2jslib.dart' show Compiler; |
| 9 import '../../elements/elements.dart' show ClassElement, FunctionElement; | 13 import '../../elements/elements.dart' show ClassElement, FunctionElement; |
| 14 import '../../hash/sha1.dart' show Hasher; |
| 15 |
| 16 import '../../io/code_output.dart'; |
| 17 |
| 18 import '../../io/line_column_provider.dart' show |
| 19 LineColumnCollector, |
| 20 LineColumnProvider; |
| 21 |
| 22 import '../../io/source_map_builder.dart' show |
| 23 SourceMapBuilder; |
| 24 |
| 10 import '../../js/js.dart' as js; | 25 import '../../js/js.dart' as js; |
| 11 import '../../js_backend/js_backend.dart' show | 26 import '../../js_backend/js_backend.dart' show |
| 12 JavaScriptBackend, | 27 JavaScriptBackend, |
| 13 Namer, | 28 Namer, |
| 14 ConstantEmitter; | 29 ConstantEmitter; |
| 15 | 30 |
| 31 import '../../util/util.dart' show |
| 32 NO_LOCATION_SPANNABLE; |
| 33 |
| 34 import '../../util/uri_extras.dart' show |
| 35 relativize; |
| 36 |
| 16 import '../js_emitter.dart' show AstContainer, NativeEmitter; | 37 import '../js_emitter.dart' show AstContainer, NativeEmitter; |
| 17 | 38 |
| 18 import 'package:js_runtime/shared/embedded_names.dart' show | 39 import 'package:js_runtime/shared/embedded_names.dart' show |
| 19 CREATE_NEW_ISOLATE, | 40 CREATE_NEW_ISOLATE, |
| 20 DEFERRED_INITIALIZED, | 41 DEFERRED_INITIALIZED, |
| 21 DEFERRED_LIBRARY_URIS, | 42 DEFERRED_LIBRARY_URIS, |
| 22 DEFERRED_LIBRARY_HASHES, | 43 DEFERRED_LIBRARY_HASHES, |
| 23 GET_TYPE_FROM_NAME, | 44 GET_TYPE_FROM_NAME, |
| 24 INITIALIZE_LOADED_HUNK, | 45 INITIALIZE_LOADED_HUNK, |
| 25 INTERCEPTORS_BY_TAG, | 46 INTERCEPTORS_BY_TAG, |
| 26 IS_HUNK_INITIALIZED, | 47 IS_HUNK_INITIALIZED, |
| 27 IS_HUNK_LOADED, | 48 IS_HUNK_LOADED, |
| 28 LEAF_TAGS, | 49 LEAF_TAGS, |
| 29 MANGLED_GLOBAL_NAMES, | 50 MANGLED_GLOBAL_NAMES, |
| 30 METADATA, | 51 METADATA, |
| 31 NATIVE_SUPERCLASS_TAG_NAME, | 52 NATIVE_SUPERCLASS_TAG_NAME, |
| 32 TYPE_TO_INTERCEPTOR_MAP, | 53 TYPE_TO_INTERCEPTOR_MAP, |
| 33 TYPES; | 54 TYPES; |
| 34 | 55 |
| 35 import '../js_emitter.dart' show NativeGenerator, buildTearOffCode; | 56 import '../js_emitter.dart' show NativeGenerator, buildTearOffCode; |
| 36 import '../model.dart'; | 57 import '../model.dart'; |
| 37 | 58 |
| 59 part 'deferred_fragment_hash.dart'; |
| 38 part 'fragment_emitter.dart'; | 60 part 'fragment_emitter.dart'; |
| 39 | 61 |
| 40 class ModelEmitter { | 62 class ModelEmitter { |
| 41 final Compiler compiler; | 63 final Compiler compiler; |
| 42 final Namer namer; | 64 final Namer namer; |
| 43 ConstantEmitter constantEmitter; | 65 ConstantEmitter constantEmitter; |
| 44 final NativeEmitter nativeEmitter; | 66 final NativeEmitter nativeEmitter; |
| 67 final bool shouldGenerateSourceMap; |
| 68 |
| 69 // The full code that is written to each hunk part-file. |
| 70 final Map<Fragment, CodeOutput> outputBuffers = <Fragment, CodeOutput>{}; |
| 71 |
| 45 | 72 |
| 46 JavaScriptBackend get backend => compiler.backend; | 73 JavaScriptBackend get backend => compiler.backend; |
| 47 | 74 |
| 48 /// For deferred loading we communicate the initializers via this global var. | 75 /// For deferred loading we communicate the initializers via this global var. |
| 49 static const String deferredInitializersGlobal = | 76 static const String deferredInitializersGlobal = |
| 50 r"$__dart_deferred_initializers__"; | 77 r"$__dart_deferred_initializers__"; |
| 51 | 78 |
| 52 static const String deferredExtension = "part.js"; | 79 static const String deferredExtension = "part.js"; |
| 53 | 80 |
| 54 static const String typeNameProperty = r"builtin$cls"; | 81 static const String typeNameProperty = r"builtin$cls"; |
| 55 | 82 |
| 56 ModelEmitter(Compiler compiler, Namer namer, this.nativeEmitter) | 83 ModelEmitter(Compiler compiler, Namer namer, this.nativeEmitter, |
| 84 this.shouldGenerateSourceMap) |
| 57 : this.compiler = compiler, | 85 : this.compiler = compiler, |
| 58 this.namer = namer { | 86 this.namer = namer { |
| 59 this.constantEmitter = new ConstantEmitter( | 87 this.constantEmitter = new ConstantEmitter( |
| 60 compiler, namer, this.generateConstantReference, | 88 compiler, namer, this.generateConstantReference, |
| 61 constantListGenerator); | 89 constantListGenerator); |
| 62 } | 90 } |
| 63 | 91 |
| 64 js.Expression constantListGenerator(js.Expression array) { | 92 js.Expression constantListGenerator(js.Expression array) { |
| 65 // TODO(floitsch): remove hard-coded name. | 93 // TODO(floitsch): remove hard-coded name. |
| 66 return js.js('makeConstList(#)', [array]); | 94 return js.js('makeConstList(#)', [array]); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 // We are only interested in the "isInlined" part, but it does not hurt to | 147 // We are only interested in the "isInlined" part, but it does not hurt to |
| 120 // test for the other predicates. | 148 // test for the other predicates. |
| 121 if (isConstantInlinedOrAlreadyEmitted(value)) { | 149 if (isConstantInlinedOrAlreadyEmitted(value)) { |
| 122 return constantEmitter.generate(value); | 150 return constantEmitter.generate(value); |
| 123 } | 151 } |
| 124 return js.js('#.#', [namer.globalObjectForConstant(value), | 152 return js.js('#.#', [namer.globalObjectForConstant(value), |
| 125 namer.constantName(value)]); | 153 namer.constantName(value)]); |
| 126 } | 154 } |
| 127 | 155 |
| 128 int emitProgram(Program program) { | 156 int emitProgram(Program program) { |
| 129 List<Fragment> fragments = program.fragments; | 157 MainFragment mainFragment = program.fragments.first; |
| 130 MainFragment mainFragment = fragments.first; | 158 List<DeferredFragment> deferredFragments = |
| 131 | 159 new List<DeferredFragment>.from(program.fragments.skip(1)); |
| 132 int totalSize = 0; | |
| 133 | 160 |
| 134 FragmentEmitter fragmentEmitter = | 161 FragmentEmitter fragmentEmitter = |
| 135 new FragmentEmitter(compiler, namer, backend, constantEmitter, this); | 162 new FragmentEmitter(compiler, namer, backend, constantEmitter, this); |
| 136 | 163 |
| 137 // We have to emit the deferred fragments first, since we need their | 164 Map<DeferredFragment, _DeferredFragmentHash> deferredHashTokens = |
| 138 // deferred hash (which depends on the output) when emitting the main | 165 new Map<DeferredFragment, _DeferredFragmentHash>(); |
| 139 // fragment. | 166 for (DeferredFragment fragment in deferredFragments) { |
| 140 List<js.Expression> fragmentsCode = fragments.skip(1).map( | 167 deferredHashTokens[fragment] = new _DeferredFragmentHash(fragment); |
| 141 (DeferredFragment deferredFragment) { | 168 } |
| 142 js.Expression types = | 169 |
| 143 program.metadataTypesForOutputUnit(deferredFragment.outputUnit); | 170 js.Statement mainCode = |
| 144 return fragmentEmitter.emitDeferredFragment( | 171 fragmentEmitter.emitMainFragment(program, deferredHashTokens); |
| 145 deferredFragment, types, program.holders); | 172 |
| 146 }).toList(); | 173 Map<DeferredFragment, js.Expression> deferredFragmentsCode = |
| 147 | 174 <DeferredFragment, js.Expression>{}; |
| 148 js.Statement mainAst = fragmentEmitter.emitMainFragment(program); | 175 |
| 176 for (DeferredFragment fragment in deferredFragments) { |
| 177 js.Expression types = |
| 178 program.metadataTypesForOutputUnit(fragment.outputUnit); |
| 179 deferredFragmentsCode[fragment] = fragmentEmitter.emitDeferredFragment( |
| 180 fragment, types, program.holders); |
| 181 } |
| 149 | 182 |
| 150 js.TokenCounter counter = new js.TokenCounter(); | 183 js.TokenCounter counter = new js.TokenCounter(); |
| 151 fragmentsCode.forEach(counter.countTokens); | 184 deferredFragmentsCode.values.forEach(counter.countTokens); |
| 152 counter.countTokens(mainAst); | 185 counter.countTokens(mainCode); |
| 153 | 186 |
| 154 program.finalizers.forEach((js.TokenFinalizer f) => f.finalizeTokens()); | 187 program.finalizers.forEach((js.TokenFinalizer f) => f.finalizeTokens()); |
| 155 | 188 |
| 156 for (int i = 0; i < fragmentsCode.length; ++i) { | 189 Map<DeferredFragment, String> hunkHashes = |
| 157 String code = js.prettyPrint(fragmentsCode[i], compiler).getText(); | 190 writeDeferredFragments(deferredFragmentsCode); |
| 158 totalSize += code.length; | 191 |
| 159 compiler.outputProvider(fragments[i+1].outputFileName, deferredExtension) | 192 // Now that we have written the deferred hunks, we can update the hash |
| 160 ..add(code) | 193 // tokens in the main-fragment. |
| 161 ..close(); | 194 deferredHashTokens.forEach((DeferredFragment key, |
| 162 } | 195 _DeferredFragmentHash token) { |
| 163 | 196 token.setHash(hunkHashes[key]); |
| 164 String mainCode = js.prettyPrint(mainAst, compiler).getText(); | 197 }); |
| 165 compiler.outputProvider(mainFragment.outputFileName, 'js') | 198 |
| 166 ..add(buildGeneratedBy(compiler)) | 199 writeMainFragment(mainFragment, mainCode); |
| 167 ..add(mainCode) | 200 |
| 168 ..close(); | 201 if (backend.requiresPreamble && |
| 169 totalSize += mainCode.length; | 202 !backend.htmlLibraryIsLoaded) { |
| 170 | 203 compiler.reportHint(NO_LOCATION_SPANNABLE, MessageKind.PREAMBLE); |
| 171 return totalSize; | 204 } |
| 172 } | 205 |
| 173 | 206 if (compiler.deferredMapUri != null) { |
| 207 writeDeferredMap(); |
| 208 } |
| 209 |
| 210 // Return the total program size. |
| 211 return outputBuffers.values.fold(0, (a, b) => a + b.length); |
| 212 } |
| 213 |
| 214 /// Generates a simple header that provides the compiler's build id. |
| 174 String buildGeneratedBy(compiler) { | 215 String buildGeneratedBy(compiler) { |
| 175 var suffix = ''; | 216 var suffix = ''; |
| 176 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}'; | 217 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}'; |
| 177 return '// Generated by dart2js (fast startup), ' | 218 return '// Generated by dart2js (fast startup), ' |
| 178 'the Dart to JavaScript compiler$suffix.\n'; | 219 'the Dart to JavaScript compiler$suffix.\n'; |
| 179 } | 220 } |
| 221 |
| 222 /// Writes all deferred fragment's code into files. |
| 223 /// |
| 224 /// Returns a map from fragment to its hashcode (as used for the deferred |
| 225 /// library code). |
| 226 /// |
| 227 /// Updates the shared [outputBuffers] field with the output. |
| 228 Map<DeferredFragment, String> writeDeferredFragments( |
| 229 Map<DeferredFragment, js.Expression> fragmentsCode) { |
| 230 Map<DeferredFragment, String> hunkHashes = <DeferredFragment, String>{}; |
| 231 |
| 232 fragmentsCode.forEach((DeferredFragment fragment, js.Expression code) { |
| 233 hunkHashes[fragment] = writeDeferredFragment(fragment, code); |
| 234 }); |
| 235 |
| 236 return hunkHashes; |
| 237 } |
| 238 |
| 239 // Writes the given [fragment]'s [code] into a file. |
| 240 // |
| 241 // Updates the shared [outputBuffers] field with the output. |
| 242 void writeMainFragment(MainFragment fragment, js.Statement code) { |
| 243 LineColumnCollector lineColumnCollector; |
| 244 List<CodeOutputListener> codeOutputListeners; |
| 245 if (shouldGenerateSourceMap) { |
| 246 lineColumnCollector = new LineColumnCollector(); |
| 247 codeOutputListeners = <CodeOutputListener>[lineColumnCollector]; |
| 248 } |
| 249 |
| 250 CodeOutput mainOutput = new StreamCodeOutput( |
| 251 compiler.outputProvider('', 'js'), |
| 252 codeOutputListeners); |
| 253 outputBuffers[fragment] = mainOutput; |
| 254 |
| 255 mainOutput.addBuffer(js.prettyPrint(code, compiler, |
| 256 monitor: compiler.dumpInfoTask)); |
| 257 |
| 258 if (shouldGenerateSourceMap) { |
| 259 mainOutput.add( |
| 260 generateSourceMapTag(compiler.sourceMapUri, compiler.outputUri)); |
| 261 } |
| 262 |
| 263 mainOutput.close(); |
| 264 |
| 265 if (shouldGenerateSourceMap) { |
| 266 outputSourceMap(mainOutput, lineColumnCollector, '', |
| 267 compiler.sourceMapUri, compiler.outputUri); |
| 268 } |
| 269 } |
| 270 |
| 271 // Writes the given [fragment]'s [code] into a file. |
| 272 // |
| 273 // Returns the deferred fragment's hash. |
| 274 // |
| 275 // Updates the shared [outputBuffers] field with the output. |
| 276 String writeDeferredFragment(DeferredFragment fragment, js.Expression code) { |
| 277 List<CodeOutputListener> outputListeners = <CodeOutputListener>[]; |
| 278 Hasher hasher = new Hasher(); |
| 279 outputListeners.add(hasher); |
| 280 |
| 281 LineColumnCollector lineColumnCollector; |
| 282 if (shouldGenerateSourceMap) { |
| 283 lineColumnCollector = new LineColumnCollector(); |
| 284 outputListeners.add(lineColumnCollector); |
| 285 } |
| 286 |
| 287 String hunkPrefix = fragment.outputFileName; |
| 288 |
| 289 CodeOutput output = new StreamCodeOutput( |
| 290 compiler.outputProvider(hunkPrefix, deferredExtension), |
| 291 outputListeners); |
| 292 |
| 293 outputBuffers[fragment] = output; |
| 294 |
| 295 // The [code] contains the function that must be invoked when the deferred |
| 296 // hunk is loaded. |
| 297 // That function must be in a map from its hashcode to the function. Since |
| 298 // we don't know the hash before we actually emit the code we store the |
| 299 // function in a temporary field first: |
| 300 // |
| 301 // deferredInitializer.current = <pretty-printed code>; |
| 302 // deferredInitializer[<hash>] = deferredInitializer.current; |
| 303 |
| 304 output.add('\n${deferredInitializersGlobal}.current = '); |
| 305 |
| 306 output.addBuffer(js.prettyPrint(code, compiler, |
| 307 monitor: compiler.dumpInfoTask)); |
| 308 |
| 309 // Make a unique hash of the code (before the sourcemaps are added) |
| 310 // This will be used to retrieve the initializing function from the global |
| 311 // variable. |
| 312 String hash = hasher.getHash(); |
| 313 |
| 314 // Now we copy the deferredInitializer.current into its correct hash. |
| 315 output.add('\n${deferredInitializersGlobal}["$hash"] = ' |
| 316 '${deferredInitializersGlobal}.current'); |
| 317 |
| 318 if (shouldGenerateSourceMap) { |
| 319 Uri mapUri, partUri; |
| 320 Uri sourceMapUri = compiler.sourceMapUri; |
| 321 Uri outputUri = compiler.outputUri; |
| 322 |
| 323 if (sourceMapUri != null) { |
| 324 String mapFileName = |
| 325 hunkPrefix + deferredExtension + ".map"; |
| 326 List<String> mapSegments = sourceMapUri.pathSegments.toList(); |
| 327 mapSegments[mapSegments.length - 1] = mapFileName; |
| 328 mapUri = compiler.sourceMapUri.replace(pathSegments: mapSegments); |
| 329 } |
| 330 |
| 331 if (outputUri != null) { |
| 332 String hunkFileName = hunkPrefix + deferredExtension; |
| 333 List<String> partSegments = outputUri.pathSegments.toList(); |
| 334 partSegments[partSegments.length - 1] = hunkFileName; |
| 335 partUri = compiler.outputUri.replace(pathSegments: partSegments); |
| 336 } |
| 337 |
| 338 output.add(generateSourceMapTag(mapUri, partUri)); |
| 339 output.close(); |
| 340 outputSourceMap(output, lineColumnCollector, hunkPrefix, mapUri, partUri); |
| 341 } else { |
| 342 output.close(); |
| 343 } |
| 344 |
| 345 return hash; |
| 346 } |
| 347 |
| 348 String generateSourceMapTag(Uri sourceMapUri, Uri fileUri) { |
| 349 if (sourceMapUri != null && fileUri != null) { |
| 350 String sourceMapFileName = relativize(fileUri, sourceMapUri, false); |
| 351 return ''' |
| 352 |
| 353 //# sourceMappingURL=$sourceMapFileName |
| 354 '''; |
| 355 } |
| 356 return ''; |
| 357 } |
| 358 |
| 359 |
| 360 void outputSourceMap(CodeOutput output, |
| 361 LineColumnProvider lineColumnProvider, |
| 362 String name, |
| 363 [Uri sourceMapUri, |
| 364 Uri fileUri]) { |
| 365 if (!shouldGenerateSourceMap) return; |
| 366 // Create a source file for the compilation output. This allows using |
| 367 // [:getLine:] to transform offsets to line numbers in [SourceMapBuilder]. |
| 368 SourceMapBuilder sourceMapBuilder = |
| 369 new SourceMapBuilder(sourceMapUri, fileUri, lineColumnProvider); |
| 370 output.forEachSourceLocation(sourceMapBuilder.addMapping); |
| 371 String sourceMap = sourceMapBuilder.build(); |
| 372 compiler.outputProvider(name, 'js.map') |
| 373 ..add(sourceMap) |
| 374 ..close(); |
| 375 } |
| 376 |
| 377 /// Writes a mapping from library-name to hunk files. |
| 378 /// |
| 379 /// The output is written into a separate file that can be used by outside |
| 380 /// tools. |
| 381 void writeDeferredMap() { |
| 382 Map<String, dynamic> mapping = new Map<String, dynamic>(); |
| 383 // Json does not support comments, so we embed the explanation in the |
| 384 // data. |
| 385 mapping["_comment"] = "This mapping shows which compiled `.js` files are " |
| 386 "needed for a given deferred library import."; |
| 387 mapping.addAll(compiler.deferredLoadTask.computeDeferredMap()); |
| 388 compiler.outputProvider(compiler.deferredMapUri.path, 'deferred_map') |
| 389 ..add(const JsonEncoder.withIndent(" ").convert(mapping)) |
| 390 ..close(); |
| 391 } |
| 180 } | 392 } |
| OLD | NEW |