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 <sra@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Stephen Adams 2018-01-05 13:44:19 -08:00 committed by commit-bot@chromium.org
parent b8b69b1bd9
commit 31cd4e1457
5 changed files with 129 additions and 83 deletions

View file

@ -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<String, List<jsAst.LiteralString>> deferredLibraryUris =
new Map<String, List<jsAst.LiteralString>>();
Map<String, List<_DeferredOutputUnitHash>> deferredLibraryHashes =
new Map<String, List<_DeferredOutputUnitHash>>();
compiler.deferredLoadTask.hunksToLoad
.forEach((String loadId, List<OutputUnit> outputUnits) {
List<jsAst.LiteralString> uris = new List<jsAst.LiteralString>();
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<String, List<jsAst.Expression>> mapping) {
List<jsAst.Property> properties = new List<jsAst.Property>();
mapping.forEach((String key, List<jsAst.Expression> 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<String, List<OutputUnit>> loadMap,
Map<OutputUnit, _DeferredOutputUnitHash> deferredLoadHashes,
void finish(jsAst.Expression map, jsAst.Expression uris,
jsAst.Expression hashes)) {
Map<OutputUnit, int> fragmentIndexes = <OutputUnit, int>{};
var uris = <jsAst.Expression>[];
var hashes = <jsAst.Expression>[];
List<jsAst.Property> libraryPartsMapEntries = <jsAst.Property>[];
loadMap.forEach((String loadId, List<OutputUnit> fragmentList) {
List<jsAst.Expression> indexes = <jsAst.Expression>[];
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<OutputUnit, jsAst.Program> buildOutputAstForDeferredCode(
Program program) {
if (!program.isSplit) return const <OutputUnit, jsAst.Program>{};

View file

@ -1256,32 +1256,6 @@ class FragmentEmitter {
List<js.Property> globals = <js.Property>[];
js.ArrayInitializer fragmentUris(List<Fragment> fragments) {
return js.stringArray(fragments.map((Fragment fragment) =>
"${fragment.outputFileName}.${ModelEmitter.deferredExtension}"));
}
js.ArrayInitializer fragmentHashes(List<Fragment> fragments) {
return new js.ArrayInitializer(fragments
.map((fragment) => deferredLoadHashes[fragment])
.toList(growable: false));
}
List<js.Property> uris = new List<js.Property>(loadMap.length);
List<js.Property> hashes = new List<js.Property>(loadMap.length);
int count = 0;
loadMap.forEach((String loadId, List<Fragment> 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<String, List<Fragment>> loadMap,
Map<DeferredFragment, _DeferredFragmentHash> deferredLoadHashes,
void finish(
js.Expression map, js.Expression uris, js.Expression hashes)) {
Map<Fragment, int> fragmentIndexes = <Fragment, int>{};
List<String> fragmentUris = <String>[];
List<js.Expression> fragmentHashes = <js.Expression>[];
List<js.Property> libraryPartsMapEntries = <js.Property>[];
loadMap.forEach((String loadId, List<Fragment> fragmentList) {
List<js.Expression> indexes = <js.Expression>[];
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

View file

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

View file

@ -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<Null> 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<String> uris = JS('JSExtendableArray|Null', '#[#]', urisMap, loadId);
if (uris == null) return new Future.value(null);
var hashesMap = JS_EMBEDDED_GLOBAL('', DEFERRED_LIBRARY_HASHES);
List<String> 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<String> uris = <String>[];
List<String> hashes = <String>[];
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);

View file

@ -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.
///