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

Side by Side Diff: pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart

Issue 1235973004: dart2js: Use the correct hashes for deferred code in the startup-emitter. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Reupload and probably some minor fixes. Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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);
Siggi Cherem (dart-lang) 2015/07/23 00:01:10 will writeDefferredFragments be used anywhere else
floitsch 2015/07/29 18:00:28 Whenever possible I try to return the result of a
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 // Return the total program size.
207 return outputBuffers.values.fold(0, (a, b) => a + b.length);
208 }
209
210 /// Generates a simple header that provides the compiler's build id.
174 String buildGeneratedBy(compiler) { 211 String buildGeneratedBy(compiler) {
175 var suffix = ''; 212 var suffix = '';
176 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}'; 213 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
177 return '// Generated by dart2js (fast startup), ' 214 return '// Generated by dart2js (fast startup), '
178 'the Dart to JavaScript compiler$suffix.\n'; 215 'the Dart to JavaScript compiler$suffix.\n';
179 } 216 }
217
218 /// Writes all deferred fragment's code into files.
219 ///
220 /// Returns a map from fragment to its hashcode (as used for the deferred
221 /// library code).
222 ///
223 /// Updates the shared [outputBuffers] field with the output.
224 Map<DeferredFragment, String> writeDeferredFragments(
225 Map<DeferredFragment, js.Expression> fragmentsCode) {
226 Map<DeferredFragment, String> hunkHashes = <DeferredFragment, String>{};
227
228 fragmentsCode.forEach((DeferredFragment fragment, js.Expression code) {
229 hunkHashes[fragment] = writeDeferredFragment(fragment, code);
230 });
231
232 return hunkHashes;
233 }
234
235 // Writes the given [fragment]'s [code] into a file.
236 //
237 // Updates the shared [outputBuffers] field with the output.
238 void writeMainFragment(MainFragment fragment, js.Statement code) {
239 LineColumnCollector lineColumnCollector;
240 List<CodeOutputListener> codeOutputListeners;
241 if (shouldGenerateSourceMap) {
242 lineColumnCollector = new LineColumnCollector();
243 codeOutputListeners = <CodeOutputListener>[lineColumnCollector];
244 }
245
246 CodeOutput mainOutput = new StreamCodeOutput(
247 compiler.outputProvider('', 'js'),
248 codeOutputListeners);
249 outputBuffers[fragment] = mainOutput;
250
251 mainOutput.addBuffer(js.prettyPrint(code, compiler,
252 monitor: compiler.dumpInfoTask));
253
254 if (compiler.deferredMapUri != null) {
Siggi Cherem (dart-lang) 2015/07/23 00:01:10 any reason we need to emit the map here in the js_
floitsch 2015/07/29 18:00:28 No reason. A lot of this code has been copied/insp
255 writeDeferredMap();
256 }
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698