mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:53:55 +00:00
[ddc] Rolling internal dart_library.js into SDK
Change-Id: Ifb1cf1aed53b04eaa0a258668eab6b4b8f19df80 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/286609 Reviewed-by: Nicholas Shahan <nshahan@google.com> Commit-Queue: Mark Zhou <markzipan@google.com>
This commit is contained in:
parent
6414db5327
commit
a92274cae8
|
@ -28,23 +28,55 @@ if (!dart_library) {
|
|||
dart_library.libraryImports = libraryImports;
|
||||
|
||||
const _metrics = Symbol('metrics');
|
||||
const _logMetrics = false;
|
||||
|
||||
// Returns a map from module name to various metrics for module.
|
||||
function metrics() {
|
||||
function moduleMetrics() {
|
||||
const map = {};
|
||||
const keys = Array.from(_libraries.keys());
|
||||
for (const key of keys) {
|
||||
const lib = _libraries.get(key);
|
||||
map[lib._name] = lib._library[_metrics];
|
||||
map[lib._name] = lib.firstLibraryValue[_metrics];
|
||||
}
|
||||
return map;
|
||||
}
|
||||
dart_library.metrics = metrics;
|
||||
dart_library.moduleMetrics = moduleMetrics;
|
||||
|
||||
// Returns an application level overview of the module metrics.
|
||||
function appMetrics() {
|
||||
const metrics = moduleMetrics();
|
||||
let dartSize = 0;
|
||||
let jsSize = 0;
|
||||
let sourceMapSize = 0;
|
||||
let evaluatedModules = 0;
|
||||
const keys = Array.from(_libraries.keys());
|
||||
|
||||
let firstLoadStart = Number.MAX_VALUE;
|
||||
let lastLoadEnd = Number.MIN_VALUE;
|
||||
|
||||
for (const module of keys) {
|
||||
let data = metrics[module];
|
||||
if (data != null) {
|
||||
evaluatedModules++;
|
||||
dartSize += data.dartSize;
|
||||
jsSize += data.jsSize;
|
||||
sourceMapSize += data.sourceMapSize;
|
||||
firstLoadStart = Math.min(firstLoadStart, data.loadStart);
|
||||
lastLoadEnd = Math.max(lastLoadEnd, data.loadEnd);
|
||||
}
|
||||
}
|
||||
return {
|
||||
'dartSize': dartSize,
|
||||
'jsSize': jsSize,
|
||||
'sourceMapSize': sourceMapSize,
|
||||
'evaluatedModules': evaluatedModules,
|
||||
'loadTimeMs': lastLoadEnd - firstLoadStart
|
||||
};
|
||||
}
|
||||
dart_library.appMetrics = appMetrics;
|
||||
|
||||
function _sortFn(key1, key2) {
|
||||
const t1 = _libraries.get(key1)._library[_metrics].loadTime;
|
||||
const t2 = _libraries.get(key2)._library[_metrics].loadTime;
|
||||
const t1 = _libraries.get(key1).firstLibraryValue[_metrics].loadStart;
|
||||
const t2 = _libraries.get(key2).firstLibraryValue[_metrics].loadStart;
|
||||
return t1 - t2;
|
||||
}
|
||||
|
||||
|
@ -52,18 +84,19 @@ if (!dart_library) {
|
|||
// in CSV format.
|
||||
function metricsCsv() {
|
||||
let buffer =
|
||||
'Module, JS Size, Dart Size, Load Time, Cumulative JS Size\n';
|
||||
'Module, JS Size, Dart Size, Load Start, Load End, Cumulative JS Size\n';
|
||||
const keys = Array.from(_libraries.keys());
|
||||
keys.sort(_sortFn);
|
||||
let cumulativeJsSize = 0;
|
||||
for (const key of keys) {
|
||||
const lib = _libraries.get(key);
|
||||
const jsSize = lib._library[_metrics].jsSize;
|
||||
const jsSize = lib.firstLibraryValue[_metrics].jsSize;
|
||||
cumulativeJsSize += jsSize;
|
||||
const dartSize = lib._library[_metrics].dartSize;
|
||||
const loadTime = lib._library[_metrics].loadTime;
|
||||
const dartSize = lib.firstLibraryValue[_metrics].dartSize;
|
||||
const loadStart = lib.firstLibraryValue[_metrics].loadStart;
|
||||
const loadEnd = lib.firstLibraryValue[_metrics].loadEnd;
|
||||
buffer += '"' + lib._name + '", ' + jsSize + ', ' + dartSize + ', ' +
|
||||
loadTime + ', ' + cumulativeJsSize + '\n';
|
||||
loadStart + ', ' + loadEnd + ', ' + cumulativeJsSize + '\n';
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
@ -103,11 +136,32 @@ if (!dart_library) {
|
|||
|
||||
let _reverseImports = new Map();
|
||||
|
||||
// Set of libraries that were not only loaded on the page but also executed.
|
||||
let _executedLibraries = new Set();
|
||||
// App name to set of libraries that were not only loaded on the page but
|
||||
// also executed.
|
||||
const _executedLibraries = new Map();
|
||||
dart_library.executedLibraryCount = function() {
|
||||
let count = 0;
|
||||
_executedLibraries.forEach(function(executedLibraries, _) {
|
||||
count += executedLibraries.size;
|
||||
});
|
||||
return count;
|
||||
};
|
||||
|
||||
// Library instance that is going to be loaded or has been loaded.
|
||||
class LibraryInstance {
|
||||
constructor(libraryValue) {
|
||||
this.libraryValue = libraryValue;
|
||||
// Cyclic import detection
|
||||
this.loadingState = LibraryLoader.NOT_LOADED;
|
||||
}
|
||||
|
||||
get isNotLoaded() {
|
||||
return this.loadingState == LibraryLoader.NOT_LOADED;
|
||||
}
|
||||
}
|
||||
|
||||
class LibraryLoader {
|
||||
constructor(name, defaultValue, imports, loader, data) {
|
||||
constructor(name, defaultLibraryValue, imports, loader, data) {
|
||||
imports.forEach(function(i) {
|
||||
let deps = _reverseImports.get(i);
|
||||
if (!deps) {
|
||||
|
@ -117,41 +171,71 @@ if (!dart_library) {
|
|||
deps.add(name);
|
||||
});
|
||||
this._name = name;
|
||||
this._library = defaultValue ? defaultValue : {};
|
||||
this._defaultLibraryValue =
|
||||
defaultLibraryValue ? defaultLibraryValue : {};
|
||||
this._imports = imports;
|
||||
this._loader = loader;
|
||||
data.jsSize = loader.toString().length;
|
||||
data.loadTime = Infinity;
|
||||
data.loadStart = NaN;
|
||||
data.loadEnd = NaN;
|
||||
this._metrics = data;
|
||||
|
||||
// Cyclic import detection
|
||||
this._state = LibraryLoader.NOT_LOADED;
|
||||
// First loaded instance for supporting logic that assumes there is only
|
||||
// one app.
|
||||
// TODO(b/204209941): Remove _firstLibraryInstance after debugger and
|
||||
// metrics support multiple apps.
|
||||
this._firstLibraryInstance =
|
||||
new LibraryInstance(this._deepCopyDefaultValue());
|
||||
this._firstLibraryInstanceUsed = false;
|
||||
|
||||
// App name to instance map.
|
||||
this._instanceMap = new Map();
|
||||
}
|
||||
|
||||
loadImports() {
|
||||
let results = [];
|
||||
for (let name of this._imports) {
|
||||
results.push(import_(name));
|
||||
}
|
||||
return results;
|
||||
/// First loaded value for supporting logic that assumes there is only
|
||||
/// one app.
|
||||
get firstLibraryValue() {
|
||||
return this._firstLibraryInstance.libraryValue;
|
||||
}
|
||||
|
||||
/// The loaded instance value for the given `appName`.
|
||||
libraryValueInApp(appName) {
|
||||
return this._instanceMap.get(appName).libraryValue;
|
||||
}
|
||||
|
||||
load(appName) {
|
||||
let instance = this._instanceMap.get(appName);
|
||||
if (!instance && !this._firstLibraryInstanceUsed) {
|
||||
// If `_firstLibraryInstance` is already assigned to an app, creates a
|
||||
// new instance clone (with deep copy) and assigns it the given app.
|
||||
// Otherwise, reuse `_firstLibraryInstance`.
|
||||
instance = this._firstLibraryInstance;
|
||||
this._firstLibraryInstanceUsed = true;
|
||||
this._instanceMap.set(appName, instance);
|
||||
}
|
||||
if (!instance) {
|
||||
instance = new LibraryInstance(this._deepCopyDefaultValue());
|
||||
this._instanceMap.set(appName, instance);
|
||||
}
|
||||
|
||||
load() {
|
||||
// Check for cycles
|
||||
if (this._state == LibraryLoader.LOADING) {
|
||||
if (instance.loadingState == LibraryLoader.LOADING) {
|
||||
throwLibraryError('Circular dependence on library: ' + this._name);
|
||||
} else if (this._state >= LibraryLoader.READY) {
|
||||
return this._library;
|
||||
} else if (instance.loadingState >= LibraryLoader.READY) {
|
||||
return instance.libraryValue;
|
||||
}
|
||||
_executedLibraries.add(this._name);
|
||||
this._state = LibraryLoader.LOADING;
|
||||
if (!_executedLibraries.has(appName)) {
|
||||
_executedLibraries.set(appName, new Set());
|
||||
}
|
||||
_executedLibraries.get(appName).add(this._name);
|
||||
instance.loadingState = LibraryLoader.LOADING;
|
||||
|
||||
// Handle imports
|
||||
let args = this.loadImports();
|
||||
let args = this._loadImports(appName);
|
||||
|
||||
// Load the library
|
||||
let loader = this;
|
||||
let library = this._library;
|
||||
let library = instance.libraryValue;
|
||||
|
||||
library[libraryImports] = this._imports;
|
||||
library[loadedModule] = library;
|
||||
|
@ -160,40 +244,50 @@ if (!dart_library) {
|
|||
|
||||
if (this._name == 'dart_sdk') {
|
||||
// Eagerly load the SDK.
|
||||
if (!!self.performance) {
|
||||
library[_metrics].loadTime = self.performance.now();
|
||||
if (!!self.performance && !!self.performance.now) {
|
||||
library[_metrics].loadStart = self.performance.now();
|
||||
}
|
||||
if (_logMetrics) console.time('Load ' + this._name);
|
||||
this._loader.apply(null, args);
|
||||
if (_logMetrics) console.timeEnd('Load ' + this._name);
|
||||
if (!!self.performance && !!self.performance.now) {
|
||||
library[_metrics].loadEnd = self.performance.now();
|
||||
}
|
||||
} else {
|
||||
// Load / parse other modules on demand.
|
||||
let done = false;
|
||||
this._library = new Proxy(library, {
|
||||
instance.libraryValue = new Proxy(library, {
|
||||
get: function(o, name) {
|
||||
if (name == _metrics) {
|
||||
return o[name];
|
||||
}
|
||||
if (!done) {
|
||||
done = true;
|
||||
if (!!self.performance) {
|
||||
library[_metrics].loadTime = self.performance.now();
|
||||
if (!!self.performance && !!self.performance.now) {
|
||||
library[_metrics].loadStart = self.performance.now();
|
||||
}
|
||||
if (_logMetrics) console.time('Load ' + loader._name);
|
||||
loader._loader.apply(null, args);
|
||||
if (_logMetrics) console.timeEnd('Load ' + loader._name);
|
||||
if (!!self.performance && !!self.performance.now) {
|
||||
library[_metrics].loadEnd = self.performance.now();
|
||||
}
|
||||
}
|
||||
return o[name];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._state = LibraryLoader.READY;
|
||||
return this._library;
|
||||
instance.loadingState = LibraryLoader.READY;
|
||||
return instance.libraryValue;
|
||||
}
|
||||
|
||||
stub() {
|
||||
return this._library;
|
||||
_loadImports(appName) {
|
||||
let results = [];
|
||||
for (let name of this._imports) {
|
||||
results.push(import_(name, appName));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
_deepCopyDefaultValue() {
|
||||
return JSON.parse(JSON.stringify(this._defaultLibraryValue));
|
||||
}
|
||||
}
|
||||
LibraryLoader.NOT_LOADED = 0;
|
||||
|
@ -208,7 +302,7 @@ if (!dart_library) {
|
|||
dart_library.debuggerLibraries = function() {
|
||||
let debuggerLibraries = [];
|
||||
_libraries.forEach(function(value, key, map) {
|
||||
debuggerLibraries.push(value.load());
|
||||
debuggerLibraries.push(value.load(_firstStartedAppName));
|
||||
});
|
||||
debuggerLibraries.__proto__ = null;
|
||||
return debuggerLibraries;
|
||||
|
@ -217,21 +311,24 @@ if (!dart_library) {
|
|||
// Invalidate a library and all things that depend on it
|
||||
function _invalidateLibrary(name) {
|
||||
let lib = _libraries.get(name);
|
||||
if (lib._state == LibraryLoader.NOT_LOADED) return;
|
||||
lib._state = LibraryLoader.NOT_LOADED;
|
||||
lib._library = {};
|
||||
if (lib._instanceMap.size === 0) return;
|
||||
lib._firstLibraryInstance =
|
||||
new LibraryInstance(lib._deepCopyDefaultValue());
|
||||
lib._firstLibraryInstanceUsed = false;
|
||||
lib._instanceMap.clear();
|
||||
let deps = _reverseImports.get(name);
|
||||
if (!deps) return;
|
||||
deps.forEach(_invalidateLibrary);
|
||||
}
|
||||
|
||||
function library(name, defaultValue, imports, loader, data = {}) {
|
||||
function library(name, defaultLibraryValue, imports, loader, data = {}) {
|
||||
let result = _libraries.get(name);
|
||||
if (result) {
|
||||
console.log('Re-loading ' + name);
|
||||
_invalidateLibrary(name);
|
||||
}
|
||||
result = new LibraryLoader(name, defaultValue, imports, loader, data);
|
||||
result =
|
||||
new LibraryLoader(name, defaultLibraryValue, imports, loader, data);
|
||||
_libraries.set(name, result);
|
||||
return result;
|
||||
}
|
||||
|
@ -240,20 +337,33 @@ if (!dart_library) {
|
|||
// Store executed modules upon reload.
|
||||
if (!!self.addEventListener && !!self.localStorage) {
|
||||
self.addEventListener('beforeunload', function(event) {
|
||||
_nameToApp.forEach(function(_, appName) {
|
||||
if (!_executedLibraries.get(appName)) {
|
||||
return;
|
||||
}
|
||||
let libraryCache = {
|
||||
'time': new Date().getTime(),
|
||||
'modules': Array.from(_executedLibraries.keys())
|
||||
'modules': Array.from(_executedLibraries.get(appName).keys()),
|
||||
};
|
||||
self.localStorage.setItem(
|
||||
'dartLibraryCache', JSON.stringify(libraryCache));
|
||||
`dartLibraryCache:${appName}`, JSON.stringify(libraryCache));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Map from module name to corresponding proxy library.
|
||||
// Map from module name to corresponding app to proxy library map.
|
||||
let _proxyLibs = new Map();
|
||||
|
||||
function import_(name) {
|
||||
let proxy = _proxyLibs.get(name);
|
||||
function import_(name, appName) {
|
||||
// For backward compatibility.
|
||||
if (!appName && _lastStartedSubapp) {
|
||||
appName = _lastStartedSubapp.appName;
|
||||
}
|
||||
|
||||
let proxy;
|
||||
if (_proxyLibs.has(name)) {
|
||||
proxy = _proxyLibs.get(name).get(appName);
|
||||
}
|
||||
if (proxy) return proxy;
|
||||
let proxyLib = new Proxy({}, {
|
||||
get: function(o, p) {
|
||||
|
@ -266,9 +376,21 @@ if (!dart_library) {
|
|||
xhr.open('GET', sourceURL, false);
|
||||
xhr.withCredentials = true;
|
||||
xhr.send();
|
||||
// Add inline policy to make eval() call Trusted Types compatible
|
||||
// when running in a TT compatible browser
|
||||
let policy = {
|
||||
createScript: function(script) {
|
||||
return script;
|
||||
}
|
||||
};
|
||||
if (self.trustedTypes && self.trustedTypes.createPolicy) {
|
||||
policy = self.trustedTypes.createPolicy(
|
||||
'dartDdcModuleLoading#dart_library', policy);
|
||||
}
|
||||
// Append sourceUrl so the resource shows up in the Chrome
|
||||
// console.
|
||||
eval(xhr.responseText + '//@ sourceURL=' + sourceURL);
|
||||
eval(policy.createScript(
|
||||
xhr.responseText + '//@ sourceURL=' + sourceURL));
|
||||
lib = _libraries.get(name);
|
||||
}
|
||||
}
|
||||
|
@ -277,10 +399,13 @@ if (!dart_library) {
|
|||
}
|
||||
// Always load the library before accessing a property as it may have
|
||||
// been invalidated.
|
||||
return lib.load()[p];
|
||||
return lib.load(appName)[p];
|
||||
}
|
||||
});
|
||||
_proxyLibs.set(name, proxyLib);
|
||||
if (!_proxyLibs.has(name)) {
|
||||
_proxyLibs.set(name, new Map());
|
||||
}
|
||||
_proxyLibs.get(name).set(appName, proxyLib);
|
||||
return proxyLib;
|
||||
}
|
||||
dart_library.import = import_;
|
||||
|
@ -297,7 +422,12 @@ if (!dart_library) {
|
|||
|
||||
let _debuggerInitialized = false;
|
||||
|
||||
// Called to initiate a hot restart of the application.
|
||||
// Caches the last N runIds to prevent hot reload requests from the same
|
||||
// runId from executing more than once.
|
||||
const _hotRestartRunIdCache = new Array();
|
||||
|
||||
// Called to initiate a hot restart of the application for a given uuid. If
|
||||
// it is not set, the last started application will be hot restarted.
|
||||
//
|
||||
// "Hot restart" means all application state is cleared, the newly compiled
|
||||
// modules are loaded, and `main()` is called.
|
||||
|
@ -316,100 +446,190 @@ if (!dart_library) {
|
|||
// 3. Call dart:_runtime's `hotRestart()` function to clear any state that
|
||||
// `dartdevc` is tracking, such as initialized static fields and type
|
||||
// caches.
|
||||
// 4. Call `window.$dartWarmReload()` (provided by the HTML page) to reload
|
||||
// the relevant JS modules, passing a callback that will invoke `main()`.
|
||||
// 5. `$dartWarmReload` calls the callback to rerun main.
|
||||
// 4. Call `self.$dartReloadModifiedModules()` (provided by the HTML page)
|
||||
// to reload the relevant JS modules, passing a callback that will invoke
|
||||
// `main()`.
|
||||
// 5. `$dartReloadModifiedModules` calls the callback to rerun main.
|
||||
//
|
||||
function reload(clearState) {
|
||||
// TODO(jmesserly): once we've rolled out `clearState` make it the
|
||||
// default, and eventually remove the parameter.
|
||||
if (clearState == null) clearState = true;
|
||||
|
||||
|
||||
// TODO(jmesserly): we may want to change these APIs to use the
|
||||
// "hot restart" terminology for consistency with Flutter. In Flutter,
|
||||
// "hot reload" refers to keeping the application state and attempting to
|
||||
// patch the code for the application while it is executing
|
||||
// (https://flutter.io/hot-reload/), whereas "hot restart" refers to what
|
||||
// dartdevc supports: tear down the app, update the code, and rerun the
|
||||
// app.
|
||||
if (!self || !self.$dartWarmReload) {
|
||||
async function hotRestart(config) {
|
||||
if (!self || !self.$dartReloadModifiedModules) {
|
||||
console.warn('Hot restart not supported in this environment.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the application's `onReloadStart()` function, if provided.
|
||||
let result;
|
||||
if (_lastLibrary && _lastLibrary.onReloadStart) {
|
||||
result = _lastLibrary.onReloadStart();
|
||||
// If `config.runId` is set (e.g. a unique build ID that represent the
|
||||
// current build and shared by multiple subapps), skip the following runs
|
||||
// with the same id.
|
||||
if (config && config.runId) {
|
||||
if (_hotRestartRunIdCache.indexOf(config.runId) >= 0) {
|
||||
// The run has already started (by other subapp or app)
|
||||
return;
|
||||
}
|
||||
_hotRestartRunIdCache.push(config.runId);
|
||||
|
||||
// Only cache the runIds for the last N runs. We assume that there are
|
||||
// less than N requests with different runId can happen in a very short
|
||||
// period of time (e.g. 1 second).
|
||||
if (_hotRestartRunIdCache.length > 10) {
|
||||
_hotRestartRunIdCache.shift();
|
||||
}
|
||||
}
|
||||
|
||||
let sdk = _libraries.get('dart_sdk');
|
||||
|
||||
/// Once the `onReloadStart()` completes, this finishes the restart.
|
||||
function finishHotRestart() {
|
||||
self.console.clear();
|
||||
if (clearState) {
|
||||
// This resets all initialized fields and clears type caches and other
|
||||
// temporary data structures used by the compiler/SDK.
|
||||
sdk._library.dart.hotRestart();
|
||||
const sdk = _libraries.get('dart_sdk');
|
||||
|
||||
// Finds out what apps and their subapps should be hot restarted in
|
||||
// their starting order.
|
||||
const dirtyAppNames = new Array();
|
||||
const dirtySubapps = new Array();
|
||||
if (config && config.runId) {
|
||||
_nameToApp.forEach(function(app, appName) {
|
||||
dirtySubapps.push(...app.uuidToSubapp.values());
|
||||
dirtyAppNames.push(appName);
|
||||
});
|
||||
} else {
|
||||
dirtySubapps.push(_lastStartedSubapp);
|
||||
dirtyAppNames.push(_lastStartedSubapp.appName);
|
||||
}
|
||||
// Call the module loader to reload the necessary modules.
|
||||
self.$dartWarmReload(() => {
|
||||
// Once the modules are loaded, rerun `main()`.
|
||||
start(_lastModuleName, _lastLibraryName, true);
|
||||
|
||||
// Invokes onReloadStart for each subapp in reversed starting order.
|
||||
const onReloadStartPromises = new Array();
|
||||
for (const subapp of dirtySubapps.reverse()) {
|
||||
// Call the application's `onReloadStart()` function, if provided.
|
||||
if (subapp.library && subapp.library.onReloadStart) {
|
||||
const result = subapp.library.onReloadStart();
|
||||
if (result && result.then) {
|
||||
let resolve;
|
||||
onReloadStartPromises.push(new Promise(function(res, _) {
|
||||
resolve = res;
|
||||
}));
|
||||
const dart = sdk.libraryValueInApp(subapp.appName).dart;
|
||||
result.then(dart.dynamic, function() {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
if (result && result.then) {
|
||||
result.then(sdk._library.dart.Dynamic)(finishHotRestart);
|
||||
} else {
|
||||
finishHotRestart();
|
||||
}
|
||||
}
|
||||
dart_library.reload = reload;
|
||||
// Reverse the subapps back to starting order.
|
||||
dirtySubapps.reverse();
|
||||
|
||||
await Promise.all(onReloadStartPromises);
|
||||
|
||||
let _lastModuleName;
|
||||
let _lastLibraryName;
|
||||
let _lastLibrary;
|
||||
let _originalBody;
|
||||
// Invokes SDK `hotRestart` to reset all initialized fields and clears
|
||||
// type caches and other temporary data structures used by the
|
||||
// compiler/SDK.
|
||||
for (const appName of dirtyAppNames) {
|
||||
sdk.libraryValueInApp(appName).dart.hotRestart();
|
||||
}
|
||||
|
||||
function start(moduleName, libraryName, isReload) {
|
||||
// Starts the subapps in their starting order.
|
||||
for (const subapp of dirtySubapps) {
|
||||
// Call the module loader to reload the necessary modules.
|
||||
self.$dartReloadModifiedModules(subapp.appName, function() {
|
||||
// Once the modules are loaded, rerun `main()`.
|
||||
start(
|
||||
subapp.appName, subapp.uuid, subapp.moduleName,
|
||||
subapp.libraryName, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
dart_library.reload = hotRestart;
|
||||
|
||||
/// An App contains one or multiple Subapps, all of the subapps share the
|
||||
/// same memory copy of library instances, and as a result they share state
|
||||
/// in Dart statics and top-level fields. There can be one or multiple Apps
|
||||
/// in a browser window, all of the Apps are isolated from each other
|
||||
/// (i.e. they create different instances even for the same module).
|
||||
class App {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
|
||||
// Subapp's uuid to subapps in initial starting order.
|
||||
// (ES6 preserves iteration order)
|
||||
this.uuidToSubapp = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
class Subapp {
|
||||
constructor(uuid, appName, moduleName, libraryName, library) {
|
||||
this.uuid = uuid;
|
||||
this.appName = appName;
|
||||
this.moduleName = moduleName;
|
||||
this.libraryName = libraryName;
|
||||
this.library = library;
|
||||
|
||||
this.originalBody = null;
|
||||
}
|
||||
}
|
||||
|
||||
// App name to App map in initial starting order.
|
||||
// (ES6 preserves iteration order)
|
||||
const _nameToApp = new Map();
|
||||
let _firstStartedAppName;
|
||||
let _lastStartedSubapp;
|
||||
|
||||
/// Starts a subapp that is identified with `uuid`, `moduleName`, and
|
||||
/// `libraryName` inside a parent app that is identified by `appName`.
|
||||
function start(appName, uuid, moduleName, libraryName, isReload) {
|
||||
console.info(
|
||||
`DDC: Subapp Module [${appName}:${moduleName}:${uuid}] is starting`);
|
||||
if (libraryName == null) libraryName = moduleName;
|
||||
_lastModuleName = moduleName;
|
||||
_lastLibraryName = libraryName;
|
||||
let library = import_(moduleName)[libraryName];
|
||||
_lastLibrary = library;
|
||||
let dart_sdk = import_('dart_sdk');
|
||||
const library = import_(moduleName, appName)[libraryName];
|
||||
|
||||
let app = _nameToApp.get(appName);
|
||||
if (!isReload) {
|
||||
if (!app) {
|
||||
app = new App(appName);
|
||||
_nameToApp.set(appName, app);
|
||||
}
|
||||
|
||||
let subapp = app.uuidToSubapp.get(uuid);
|
||||
if (!subapp) {
|
||||
subapp = new Subapp(uuid, appName, moduleName, libraryName, library);
|
||||
app.uuidToSubapp.set(uuid, subapp);
|
||||
}
|
||||
|
||||
_lastStartedSubapp = subapp;
|
||||
if (!_firstStartedAppName) {
|
||||
_firstStartedAppName = appName;
|
||||
}
|
||||
}
|
||||
|
||||
const subapp = app.uuidToSubapp.get(uuid);
|
||||
const sdk = import_('dart_sdk', appName);
|
||||
|
||||
if (!_debuggerInitialized) {
|
||||
// This import is only needed for chrome debugging. We should provide an
|
||||
// option to compile without it.
|
||||
dart_sdk._debugger.registerDevtoolsFormatter();
|
||||
sdk._debugger.registerDevtoolsFormatter();
|
||||
|
||||
// Create isolate.
|
||||
_debuggerInitialized = true;
|
||||
}
|
||||
if (isReload) {
|
||||
// subapp may have been modified during reload, `subapp.library` needs
|
||||
// to always point to the latest data.
|
||||
subapp.library = library;
|
||||
|
||||
if (library.onReloadEnd) {
|
||||
library.onReloadEnd();
|
||||
return;
|
||||
} else {
|
||||
if (!!self.document) {
|
||||
// Note: we expect _originalBody to be undefined in non-browser
|
||||
// Note: we expect originalBody to be undefined in non-browser
|
||||
// environments, but in that case so is the body.
|
||||
if (!_originalBody && !!self.document.body) {
|
||||
if (!subapp.originalBody && !!self.document.body) {
|
||||
self.console.warn('No body saved to update on reload');
|
||||
} else {
|
||||
self.document.body = _originalBody;
|
||||
self.document.body = subapp.originalBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If not a reload then store the initial html to reset it on reload.
|
||||
if (!!self.document && !!self.document.body) {
|
||||
_originalBody = self.document.body.cloneNode(true);
|
||||
// If not a reload and `onReloadEnd` is not defined, store the initial
|
||||
// html to reset it on reload.
|
||||
if (!library.onReloadEnd && !!self.document && !!self.document.body) {
|
||||
subapp.originalBody = self.document.body.cloneNode(true);
|
||||
}
|
||||
}
|
||||
library.main([]);
|
||||
|
|
|
@ -350,6 +350,8 @@ class TestDriver {
|
|||
var bootstrapFile = File(htmlBootstrapper.toFilePath())..createSync();
|
||||
var moduleName = compiler.metadata!.name;
|
||||
var mainLibraryName = compiler.metadataForLibraryUri(input).name;
|
||||
var appName = p.relative(
|
||||
p.withoutExtension(compiler.metadataForLibraryUri(input).importUri));
|
||||
|
||||
switch (setup.moduleFormat) {
|
||||
case ModuleFormat.ddc:
|
||||
|
@ -368,6 +370,9 @@ class TestDriver {
|
|||
var dartLibraryPath =
|
||||
escaped(p.join(ddcPath, 'lib', 'js', 'legacy', 'dart_library.js'));
|
||||
var outputPath = output.toFilePath();
|
||||
// This is used in the DDC module system for multiapp workflows and is
|
||||
// stubbed here.
|
||||
var uuid = '00000000-0000-0000-0000-000000000000';
|
||||
bootstrapFile.writeAsStringSync('''
|
||||
<script src='$dartLibraryPath'></script>
|
||||
<script src='$dartSdkPath'></script>
|
||||
|
@ -384,7 +389,8 @@ class TestDriver {
|
|||
sdk.dart.nonNullAsserts(true);
|
||||
sdk.dart.nativeNonNullAsserts(true);
|
||||
sdk._debugger.registerDevtoolsFormatter();
|
||||
dart_library.start('$moduleName', '$mainLibraryName');
|
||||
dart_library.start('$appName', '$uuid', '$moduleName', '$mainLibraryName',
|
||||
false);
|
||||
</script>
|
||||
''');
|
||||
break;
|
||||
|
|
|
@ -136,6 +136,14 @@ void main(List<String> args) async {
|
|||
var libname =
|
||||
js_names.pathToJSIdentifier(p.relative(p.withoutExtension(entry)));
|
||||
|
||||
// This is used in the DDC module system and usually corresponds to the
|
||||
// entrypoint's path.
|
||||
var appname = p.relative(p.withoutExtension(entry));
|
||||
|
||||
// This is used in the DDC module system for multiapp workflows and is
|
||||
// stubbed in ddb.
|
||||
var uuid = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
// By default (no `-d`), we use the `dartdevc` binary on the user's path to
|
||||
// compute the SDK we use for execution. I.e., we assume that `dart` is
|
||||
// under `$DART_SDK/bin/dart` and use that to find `dartdevc` and related
|
||||
|
@ -353,7 +361,7 @@ sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
|
|||
// Invoke main through the d8 preamble to ensure the code is running
|
||||
// within the fake event loop.
|
||||
self.dartMainRunner(function () {
|
||||
dart_library.start("$basename", "$libname");
|
||||
dart_library.start("$appname", "$uuid", "$basename", "$libname", false);
|
||||
});
|
||||
''';
|
||||
var dart2jsD8Preamble =
|
||||
|
|
Loading…
Reference in a new issue