From 31cd4e145744a46d6ff773edfff470898c9ea87a Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Fri, 5 Jan 2018 13:44:19 -0800 Subject: [PATCH] Factor deferred loading data 'Normalize' the data used for loading deferred libraries. Replace URIs and SHA1 hashes with indexes so that the URIs and hashes occur once. Bug: https://github.com/dart-lang/sdk/issues/29635 Change-Id: I3ac8791ad44ca588cbb1bd7d86f18243c956a04a Reviewed-on: https://dart-review.googlesource.com/32505 Commit-Queue: Stephen Adams Reviewed-by: Sigmund Cherem --- .../src/js_emitter/full_emitter/emitter.dart | 84 +++++++++++-------- .../startup_emitter/fragment_emitter.dart | 68 +++++++++------ .../startup_emitter/model_emitter.dart | 5 +- .../_internal/js_runtime/lib/js_helper.dart | 28 ++++--- .../js_runtime/lib/shared/embedded_names.dart | 27 ++++-- 5 files changed, 129 insertions(+), 83 deletions(-) diff --git a/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart index c96a17cda45..4cd70148ea0 100644 --- a/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart @@ -851,7 +851,7 @@ class Emitter extends js_emitter.EmitterBase { jsAst.Expression laziesAccess = generateEmbeddedGlobalAccess(embeddedNames.LAZIES); - return js.statement(''' + return js.statement(""" function init() { $isolatePropertiesName = Object.create(null); #allClasses = map(); @@ -955,7 +955,7 @@ class Emitter extends js_emitter.EmitterBase { return Isolate; } - }''', { + }""", { 'allClasses': allClassesAccess, 'getTypeFromName': getTypeFromNameAccess, 'interceptorsByTag': interceptorsByTagAccess, @@ -1735,49 +1735,59 @@ class Emitter extends js_emitter.EmitterBase { generateEmbeddedGlobalAccess(embeddedNames.DEFERRED_INITIALIZED) })); - // Write a javascript mapping from Deferred import load ids (derrived - // from the import prefix.) to a list of lists of uris of hunks to load, - // and a corresponding mapping to a list of hashes used by - // INITIALIZE_LOADED_HUNK and IS_HUNK_LOADED. - Map> deferredLibraryUris = - new Map>(); - Map> deferredLibraryHashes = - new Map>(); - compiler.deferredLoadTask.hunksToLoad - .forEach((String loadId, List outputUnits) { - List uris = new List(); - List<_DeferredOutputUnitHash> hashes = - new List<_DeferredOutputUnitHash>(); - deferredLibraryHashes[loadId] = new List<_DeferredOutputUnitHash>(); - for (OutputUnit outputUnit in outputUnits) { - uris.add(js.escapedString( - compiler.deferredLoadTask.deferredPartFileName(outputUnit.name))); - hashes.add(deferredLoadHashes[outputUnit]); + void store( + jsAst.Expression map, jsAst.Expression uris, jsAst.Expression hashes) { + void assign(String name, jsAst.Expression value) { + parts.add( + js.statement('# = #', [generateEmbeddedGlobalAccess(name), value])); } - deferredLibraryUris[loadId] = uris; - deferredLibraryHashes[loadId] = hashes; - }); - - void emitMapping(String name, Map> mapping) { - List properties = new List(); - mapping.forEach((String key, List values) { - properties.add(new jsAst.Property( - js.escapedString(key), new jsAst.ArrayInitializer(values))); - }); - jsAst.Node initializer = - new jsAst.ObjectInitializer(properties, isOneLiner: true); - - jsAst.Node globalName = generateEmbeddedGlobalAccess(name); - parts.add(js.statement("# = #", [globalName, initializer])); + assign(embeddedNames.DEFERRED_LIBRARY_PARTS, map); + assign(embeddedNames.DEFERRED_PART_URIS, uris); + assign(embeddedNames.DEFERRED_PART_HASHES, hashes); } - emitMapping(embeddedNames.DEFERRED_LIBRARY_URIS, deferredLibraryUris); - emitMapping(embeddedNames.DEFERRED_LIBRARY_HASHES, deferredLibraryHashes); + createDeferredLoadingData( + compiler.deferredLoadTask.hunksToLoad, deferredLoadHashes, store); return new jsAst.Block(parts); } + // Create data used for loading and initializing the hunks for a deferred + // import. There are three parts: a map from loadId to list of parts, where + // parts are represented as an index; an array of uris indexed by part; and an + // array of hashes indexed by part. + void createDeferredLoadingData( + Map> loadMap, + Map deferredLoadHashes, + void finish(jsAst.Expression map, jsAst.Expression uris, + jsAst.Expression hashes)) { + Map fragmentIndexes = {}; + var uris = []; + var hashes = []; + + List libraryPartsMapEntries = []; + + loadMap.forEach((String loadId, List fragmentList) { + List indexes = []; + for (OutputUnit fragment in fragmentList) { + int index = fragmentIndexes[fragment]; + if (index == null) { + index = fragmentIndexes[fragment] = fragmentIndexes.length; + uris.add(js.escapedString( + compiler.deferredLoadTask.deferredPartFileName(fragment.name))); + hashes.add(deferredLoadHashes[fragment]); + } + indexes.add(js.number(index)); + } + libraryPartsMapEntries.add(new jsAst.Property( + js.string(loadId), new jsAst.ArrayInitializer(indexes))); + }); + + finish(new jsAst.ObjectInitializer(libraryPartsMapEntries), + new jsAst.ArrayInitializer(uris), new jsAst.ArrayInitializer(hashes)); + } + Map buildOutputAstForDeferredCode( Program program) { if (!program.isSplit) return const {}; diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart index 13cfaef992a..5be4cd731b1 100644 --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart @@ -1256,32 +1256,6 @@ class FragmentEmitter { List globals = []; - js.ArrayInitializer fragmentUris(List fragments) { - return js.stringArray(fragments.map((Fragment fragment) => - "${fragment.outputFileName}.${ModelEmitter.deferredExtension}")); - } - - js.ArrayInitializer fragmentHashes(List fragments) { - return new js.ArrayInitializer(fragments - .map((fragment) => deferredLoadHashes[fragment]) - .toList(growable: false)); - } - - List uris = new List(loadMap.length); - List hashes = new List(loadMap.length); - int count = 0; - loadMap.forEach((String loadId, List fragmentList) { - uris[count] = - new js.Property(js.string(loadId), fragmentUris(fragmentList)); - hashes[count] = - new js.Property(js.string(loadId), fragmentHashes(fragmentList)); - count++; - }); - - globals.add(new js.Property( - js.string(DEFERRED_LIBRARY_URIS), new js.ObjectInitializer(uris))); - globals.add(new js.Property( - js.string(DEFERRED_LIBRARY_HASHES), new js.ObjectInitializer(hashes))); globals.add(new js.Property( js.string(DEFERRED_INITIALIZED), js.js("Object.create(null)"))); @@ -1316,9 +1290,51 @@ class FragmentEmitter { globals.add(new js.Property( js.string(INITIALIZE_LOADED_HUNK), initializeLoadedHunkFunction)); + createDeferredLoadingData(loadMap, deferredLoadHashes, + (js.Expression map, js.Expression uris, js.Expression hashes) { + globals.add(new js.Property(js.string(DEFERRED_LIBRARY_PARTS), map)); + globals.add(new js.Property(js.string(DEFERRED_PART_URIS), uris)); + globals.add(new js.Property(js.string(DEFERRED_PART_HASHES), hashes)); + }); + return globals; } + // Create data used for loading and initializing the hunks for a deferred + // import. There are three parts: a map from loadId to list of parts, where + // parts are represented as an index; an array of uris indexed by part; and an + // array of hashes indexed by part. + static void createDeferredLoadingData( + Map> loadMap, + Map deferredLoadHashes, + void finish( + js.Expression map, js.Expression uris, js.Expression hashes)) { + Map fragmentIndexes = {}; + List fragmentUris = []; + List fragmentHashes = []; + + List libraryPartsMapEntries = []; + + loadMap.forEach((String loadId, List fragmentList) { + List indexes = []; + for (Fragment fragment in fragmentList) { + int index = fragmentIndexes[fragment]; + if (index == null) { + index = fragmentIndexes[fragment] = fragmentIndexes.length; + fragmentUris.add( + "${fragment.outputFileName}.${ModelEmitter.deferredExtension}"); + fragmentHashes.add(deferredLoadHashes[fragment]); + } + indexes.add(js.number(index)); + } + libraryPartsMapEntries.add( + new js.Property(js.string(loadId), new js.ArrayInitializer(indexes))); + }); + + finish(new js.ObjectInitializer(libraryPartsMapEntries), + js.stringArray(fragmentUris), new js.ArrayInitializer(fragmentHashes)); + } + /// Emits the [MANGLED_GLOBAL_NAMES] embedded global. /// /// This global maps minified names for selected classes (some important diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart index 9f50a9b2d16..86f5d3d657a 100644 --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart @@ -13,8 +13,9 @@ import 'package:js_runtime/shared/embedded_names.dart' CLASS_ID_EXTRACTOR, CREATE_NEW_ISOLATE, DEFERRED_INITIALIZED, - DEFERRED_LIBRARY_URIS, - DEFERRED_LIBRARY_HASHES, + DEFERRED_LIBRARY_PARTS, + DEFERRED_PART_URIS, + DEFERRED_PART_HASHES, GET_TYPE_FROM_NAME, INITIALIZE_EMPTY_INSTANCE, INITIALIZE_LOADED_HUNK, diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart index b2603af24c0..87e4b435d1f 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart @@ -6,8 +6,9 @@ library _js_helper; import 'dart:_js_embedded_names' show - DEFERRED_LIBRARY_URIS, - DEFERRED_LIBRARY_HASHES, + DEFERRED_LIBRARY_PARTS, + DEFERRED_PART_URIS, + DEFERRED_PART_HASHES, GET_TYPE_FROM_NAME, GET_ISOLATE_TAG, INITIALIZE_LOADED_HUNK, @@ -3642,14 +3643,21 @@ typedef void DeferredLoadCallback(); DeferredLoadCallback deferredLoadHook; Future loadDeferredLibrary(String loadId) { - // For each loadId there is a list of hunk-uris to load, and a corresponding - // list of hashes. These are stored in the app-global scope. - var urisMap = JS_EMBEDDED_GLOBAL('', DEFERRED_LIBRARY_URIS); - List uris = JS('JSExtendableArray|Null', '#[#]', urisMap, loadId); - if (uris == null) return new Future.value(null); - - var hashesMap = JS_EMBEDDED_GLOBAL('', DEFERRED_LIBRARY_HASHES); - List hashes = JS('JSExtendableArray|Null', '#[#]', hashesMap, loadId); + // For each loadId there is a list of parts to load. The parts are represented + // by an index. There are two arrays, one that maps the index into a Uri and + // another that maps the index to a hash. + var partsMap = JS_EMBEDDED_GLOBAL('', DEFERRED_LIBRARY_PARTS); + List indexes = JS('JSExtendableArray|Null', '#[#]', partsMap, loadId); + if (indexes == null) return new Future.value(null); + List uris = []; + List hashes = []; + List index2uri = JS_EMBEDDED_GLOBAL('JSArray', DEFERRED_PART_URIS); + List index2hash = JS_EMBEDDED_GLOBAL('JSArray', DEFERRED_PART_HASHES); + for (int i = 0; i < indexes.length; i++) { + int index = JS('int', '#[#]', indexes, i); + uris.add(JS('String', '#[#]', index2uri, index)); + hashes.add(JS('String', '#[#]', index2hash, index)); + } int total = hashes.length; assert(total == uris.length); diff --git a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart index e1eb66d84e9..174333c965b 100644 --- a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart +++ b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart @@ -175,23 +175,34 @@ const CLASS_FIELDS_EXTRACTOR = 'classFieldsExtractor'; /// This embedded global is used for deserialization in the isolate-library. const INITIALIZE_EMPTY_INSTANCE = "initializeEmptyInstance"; -/// Returns a map from load-ids to URIs. +/// Contains a map from load-ids to lists of part indexes. /// /// To load the deferred library that is represented by the load-id, the runtime -/// must load all associated URIs. +/// must load all associated URIs (named in DEFERRED_PART_URIS) and initialize +/// all the loaded hunks (DEFERRED_PART_HASHES). /// /// This embedded global is only used for deferred loading. -const DEFERRED_LIBRARY_URIS = 'deferredLibraryUris'; +const DEFERRED_LIBRARY_PARTS = 'deferredLibraryParts'; -/// Returns a map from load-ids to hashes. +/// Contains a list of URIs (Strings), indexed by part. +/// +/// The lists in the DEFERRED_LIBRARY_PARTS map contain indexes into this list. +/// +/// This embedded global is only used for deferred loading. +const DEFERRED_PART_URIS = 'deferredPartUris'; + +/// Contains a list of hashes, indexed by part. +/// +/// The lists in the DEFERRED_LIBRARY_PARTS map contain indexes into this list. /// /// The hashes are associated with the URIs of the load-ids (see -/// [DEFERRED_LIBRARY_URIS]). They are MD5 (or similar) hashes of the code that -/// must be loaded. By using cryptographic hashes we can avoid loading similar -/// code multiple times. +/// [DEFERRED_PART_URIS]). They are SHA1 (or similar) hashes of the code that +/// must be loaded. By using cryptographic hashes we can (1) handle loading in +/// the same web page the parts from multiple Dart applications (2) avoid +/// loading similar code multiple times. /// /// This embedded global is only used for deferred loading. -const DEFERRED_LIBRARY_HASHES = 'deferredLibraryHashes'; +const DEFERRED_PART_HASHES = 'deferredPartHashes'; /// Initialize a loaded hunk. ///