mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:42:11 +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;
|
dart_library.libraryImports = libraryImports;
|
||||||
|
|
||||||
const _metrics = Symbol('metrics');
|
const _metrics = Symbol('metrics');
|
||||||
const _logMetrics = false;
|
|
||||||
|
|
||||||
// Returns a map from module name to various metrics for module.
|
// Returns a map from module name to various metrics for module.
|
||||||
function metrics() {
|
function moduleMetrics() {
|
||||||
const map = {};
|
const map = {};
|
||||||
const keys = Array.from(_libraries.keys());
|
const keys = Array.from(_libraries.keys());
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const lib = _libraries.get(key);
|
const lib = _libraries.get(key);
|
||||||
map[lib._name] = lib._library[_metrics];
|
map[lib._name] = lib.firstLibraryValue[_metrics];
|
||||||
}
|
}
|
||||||
return map;
|
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) {
|
function _sortFn(key1, key2) {
|
||||||
const t1 = _libraries.get(key1)._library[_metrics].loadTime;
|
const t1 = _libraries.get(key1).firstLibraryValue[_metrics].loadStart;
|
||||||
const t2 = _libraries.get(key2)._library[_metrics].loadTime;
|
const t2 = _libraries.get(key2).firstLibraryValue[_metrics].loadStart;
|
||||||
return t1 - t2;
|
return t1 - t2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,18 +84,19 @@ if (!dart_library) {
|
||||||
// in CSV format.
|
// in CSV format.
|
||||||
function metricsCsv() {
|
function metricsCsv() {
|
||||||
let buffer =
|
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());
|
const keys = Array.from(_libraries.keys());
|
||||||
keys.sort(_sortFn);
|
keys.sort(_sortFn);
|
||||||
let cumulativeJsSize = 0;
|
let cumulativeJsSize = 0;
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const lib = _libraries.get(key);
|
const lib = _libraries.get(key);
|
||||||
const jsSize = lib._library[_metrics].jsSize;
|
const jsSize = lib.firstLibraryValue[_metrics].jsSize;
|
||||||
cumulativeJsSize += jsSize;
|
cumulativeJsSize += jsSize;
|
||||||
const dartSize = lib._library[_metrics].dartSize;
|
const dartSize = lib.firstLibraryValue[_metrics].dartSize;
|
||||||
const loadTime = lib._library[_metrics].loadTime;
|
const loadStart = lib.firstLibraryValue[_metrics].loadStart;
|
||||||
|
const loadEnd = lib.firstLibraryValue[_metrics].loadEnd;
|
||||||
buffer += '"' + lib._name + '", ' + jsSize + ', ' + dartSize + ', ' +
|
buffer += '"' + lib._name + '", ' + jsSize + ', ' + dartSize + ', ' +
|
||||||
loadTime + ', ' + cumulativeJsSize + '\n';
|
loadStart + ', ' + loadEnd + ', ' + cumulativeJsSize + '\n';
|
||||||
}
|
}
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
@ -103,11 +136,32 @@ if (!dart_library) {
|
||||||
|
|
||||||
let _reverseImports = new Map();
|
let _reverseImports = new Map();
|
||||||
|
|
||||||
// Set of libraries that were not only loaded on the page but also executed.
|
// App name to set of libraries that were not only loaded on the page but
|
||||||
let _executedLibraries = new Set();
|
// 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 {
|
class LibraryLoader {
|
||||||
constructor(name, defaultValue, imports, loader, data) {
|
constructor(name, defaultLibraryValue, imports, loader, data) {
|
||||||
imports.forEach(function(i) {
|
imports.forEach(function(i) {
|
||||||
let deps = _reverseImports.get(i);
|
let deps = _reverseImports.get(i);
|
||||||
if (!deps) {
|
if (!deps) {
|
||||||
|
@ -117,41 +171,71 @@ if (!dart_library) {
|
||||||
deps.add(name);
|
deps.add(name);
|
||||||
});
|
});
|
||||||
this._name = name;
|
this._name = name;
|
||||||
this._library = defaultValue ? defaultValue : {};
|
this._defaultLibraryValue =
|
||||||
|
defaultLibraryValue ? defaultLibraryValue : {};
|
||||||
this._imports = imports;
|
this._imports = imports;
|
||||||
this._loader = loader;
|
this._loader = loader;
|
||||||
data.jsSize = loader.toString().length;
|
data.jsSize = loader.toString().length;
|
||||||
data.loadTime = Infinity;
|
data.loadStart = NaN;
|
||||||
|
data.loadEnd = NaN;
|
||||||
this._metrics = data;
|
this._metrics = data;
|
||||||
|
|
||||||
// Cyclic import detection
|
// First loaded instance for supporting logic that assumes there is only
|
||||||
this._state = LibraryLoader.NOT_LOADED;
|
// 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() {
|
/// First loaded value for supporting logic that assumes there is only
|
||||||
let results = [];
|
/// one app.
|
||||||
for (let name of this._imports) {
|
get firstLibraryValue() {
|
||||||
results.push(import_(name));
|
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);
|
||||||
}
|
}
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
load() {
|
|
||||||
// Check for cycles
|
// Check for cycles
|
||||||
if (this._state == LibraryLoader.LOADING) {
|
if (instance.loadingState == LibraryLoader.LOADING) {
|
||||||
throwLibraryError('Circular dependence on library: ' + this._name);
|
throwLibraryError('Circular dependence on library: ' + this._name);
|
||||||
} else if (this._state >= LibraryLoader.READY) {
|
} else if (instance.loadingState >= LibraryLoader.READY) {
|
||||||
return this._library;
|
return instance.libraryValue;
|
||||||
}
|
}
|
||||||
_executedLibraries.add(this._name);
|
if (!_executedLibraries.has(appName)) {
|
||||||
this._state = LibraryLoader.LOADING;
|
_executedLibraries.set(appName, new Set());
|
||||||
|
}
|
||||||
|
_executedLibraries.get(appName).add(this._name);
|
||||||
|
instance.loadingState = LibraryLoader.LOADING;
|
||||||
|
|
||||||
// Handle imports
|
// Handle imports
|
||||||
let args = this.loadImports();
|
let args = this._loadImports(appName);
|
||||||
|
|
||||||
// Load the library
|
// Load the library
|
||||||
let loader = this;
|
let loader = this;
|
||||||
let library = this._library;
|
let library = instance.libraryValue;
|
||||||
|
|
||||||
library[libraryImports] = this._imports;
|
library[libraryImports] = this._imports;
|
||||||
library[loadedModule] = library;
|
library[loadedModule] = library;
|
||||||
|
@ -160,40 +244,50 @@ if (!dart_library) {
|
||||||
|
|
||||||
if (this._name == 'dart_sdk') {
|
if (this._name == 'dart_sdk') {
|
||||||
// Eagerly load the SDK.
|
// Eagerly load the SDK.
|
||||||
if (!!self.performance) {
|
if (!!self.performance && !!self.performance.now) {
|
||||||
library[_metrics].loadTime = self.performance.now();
|
library[_metrics].loadStart = self.performance.now();
|
||||||
}
|
}
|
||||||
if (_logMetrics) console.time('Load ' + this._name);
|
|
||||||
this._loader.apply(null, args);
|
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 {
|
} else {
|
||||||
// Load / parse other modules on demand.
|
// Load / parse other modules on demand.
|
||||||
let done = false;
|
let done = false;
|
||||||
this._library = new Proxy(library, {
|
instance.libraryValue = new Proxy(library, {
|
||||||
get: function(o, name) {
|
get: function(o, name) {
|
||||||
if (name == _metrics) {
|
if (name == _metrics) {
|
||||||
return o[name];
|
return o[name];
|
||||||
}
|
}
|
||||||
if (!done) {
|
if (!done) {
|
||||||
done = true;
|
done = true;
|
||||||
if (!!self.performance) {
|
if (!!self.performance && !!self.performance.now) {
|
||||||
library[_metrics].loadTime = self.performance.now();
|
library[_metrics].loadStart = self.performance.now();
|
||||||
}
|
}
|
||||||
if (_logMetrics) console.time('Load ' + loader._name);
|
|
||||||
loader._loader.apply(null, args);
|
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];
|
return o[name];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this._state = LibraryLoader.READY;
|
instance.loadingState = LibraryLoader.READY;
|
||||||
return this._library;
|
return instance.libraryValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
stub() {
|
_loadImports(appName) {
|
||||||
return this._library;
|
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;
|
LibraryLoader.NOT_LOADED = 0;
|
||||||
|
@ -208,7 +302,7 @@ if (!dart_library) {
|
||||||
dart_library.debuggerLibraries = function() {
|
dart_library.debuggerLibraries = function() {
|
||||||
let debuggerLibraries = [];
|
let debuggerLibraries = [];
|
||||||
_libraries.forEach(function(value, key, map) {
|
_libraries.forEach(function(value, key, map) {
|
||||||
debuggerLibraries.push(value.load());
|
debuggerLibraries.push(value.load(_firstStartedAppName));
|
||||||
});
|
});
|
||||||
debuggerLibraries.__proto__ = null;
|
debuggerLibraries.__proto__ = null;
|
||||||
return debuggerLibraries;
|
return debuggerLibraries;
|
||||||
|
@ -217,21 +311,24 @@ if (!dart_library) {
|
||||||
// Invalidate a library and all things that depend on it
|
// Invalidate a library and all things that depend on it
|
||||||
function _invalidateLibrary(name) {
|
function _invalidateLibrary(name) {
|
||||||
let lib = _libraries.get(name);
|
let lib = _libraries.get(name);
|
||||||
if (lib._state == LibraryLoader.NOT_LOADED) return;
|
if (lib._instanceMap.size === 0) return;
|
||||||
lib._state = LibraryLoader.NOT_LOADED;
|
lib._firstLibraryInstance =
|
||||||
lib._library = {};
|
new LibraryInstance(lib._deepCopyDefaultValue());
|
||||||
|
lib._firstLibraryInstanceUsed = false;
|
||||||
|
lib._instanceMap.clear();
|
||||||
let deps = _reverseImports.get(name);
|
let deps = _reverseImports.get(name);
|
||||||
if (!deps) return;
|
if (!deps) return;
|
||||||
deps.forEach(_invalidateLibrary);
|
deps.forEach(_invalidateLibrary);
|
||||||
}
|
}
|
||||||
|
|
||||||
function library(name, defaultValue, imports, loader, data = {}) {
|
function library(name, defaultLibraryValue, imports, loader, data = {}) {
|
||||||
let result = _libraries.get(name);
|
let result = _libraries.get(name);
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log('Re-loading ' + name);
|
console.log('Re-loading ' + name);
|
||||||
_invalidateLibrary(name);
|
_invalidateLibrary(name);
|
||||||
}
|
}
|
||||||
result = new LibraryLoader(name, defaultValue, imports, loader, data);
|
result =
|
||||||
|
new LibraryLoader(name, defaultLibraryValue, imports, loader, data);
|
||||||
_libraries.set(name, result);
|
_libraries.set(name, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -240,20 +337,33 @@ if (!dart_library) {
|
||||||
// Store executed modules upon reload.
|
// Store executed modules upon reload.
|
||||||
if (!!self.addEventListener && !!self.localStorage) {
|
if (!!self.addEventListener && !!self.localStorage) {
|
||||||
self.addEventListener('beforeunload', function(event) {
|
self.addEventListener('beforeunload', function(event) {
|
||||||
let libraryCache = {
|
_nameToApp.forEach(function(_, appName) {
|
||||||
'time': new Date().getTime(),
|
if (!_executedLibraries.get(appName)) {
|
||||||
'modules': Array.from(_executedLibraries.keys())
|
return;
|
||||||
};
|
}
|
||||||
self.localStorage.setItem(
|
let libraryCache = {
|
||||||
'dartLibraryCache', JSON.stringify(libraryCache));
|
'time': new Date().getTime(),
|
||||||
|
'modules': Array.from(_executedLibraries.get(appName).keys()),
|
||||||
|
};
|
||||||
|
self.localStorage.setItem(
|
||||||
|
`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();
|
let _proxyLibs = new Map();
|
||||||
|
|
||||||
function import_(name) {
|
function import_(name, appName) {
|
||||||
let proxy = _proxyLibs.get(name);
|
// 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;
|
if (proxy) return proxy;
|
||||||
let proxyLib = new Proxy({}, {
|
let proxyLib = new Proxy({}, {
|
||||||
get: function(o, p) {
|
get: function(o, p) {
|
||||||
|
@ -266,9 +376,21 @@ if (!dart_library) {
|
||||||
xhr.open('GET', sourceURL, false);
|
xhr.open('GET', sourceURL, false);
|
||||||
xhr.withCredentials = true;
|
xhr.withCredentials = true;
|
||||||
xhr.send();
|
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
|
// Append sourceUrl so the resource shows up in the Chrome
|
||||||
// console.
|
// console.
|
||||||
eval(xhr.responseText + '//@ sourceURL=' + sourceURL);
|
eval(policy.createScript(
|
||||||
|
xhr.responseText + '//@ sourceURL=' + sourceURL));
|
||||||
lib = _libraries.get(name);
|
lib = _libraries.get(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,10 +399,13 @@ if (!dart_library) {
|
||||||
}
|
}
|
||||||
// Always load the library before accessing a property as it may have
|
// Always load the library before accessing a property as it may have
|
||||||
// been invalidated.
|
// 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;
|
return proxyLib;
|
||||||
}
|
}
|
||||||
dart_library.import = import_;
|
dart_library.import = import_;
|
||||||
|
@ -297,7 +422,12 @@ if (!dart_library) {
|
||||||
|
|
||||||
let _debuggerInitialized = false;
|
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
|
// "Hot restart" means all application state is cleared, the newly compiled
|
||||||
// modules are loaded, and `main()` is called.
|
// 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
|
// 3. Call dart:_runtime's `hotRestart()` function to clear any state that
|
||||||
// `dartdevc` is tracking, such as initialized static fields and type
|
// `dartdevc` is tracking, such as initialized static fields and type
|
||||||
// caches.
|
// caches.
|
||||||
// 4. Call `window.$dartWarmReload()` (provided by the HTML page) to reload
|
// 4. Call `self.$dartReloadModifiedModules()` (provided by the HTML page)
|
||||||
// the relevant JS modules, passing a callback that will invoke `main()`.
|
// to reload the relevant JS modules, passing a callback that will invoke
|
||||||
// 5. `$dartWarmReload` calls the callback to rerun main.
|
// `main()`.
|
||||||
|
// 5. `$dartReloadModifiedModules` calls the callback to rerun main.
|
||||||
//
|
//
|
||||||
function reload(clearState) {
|
async function hotRestart(config) {
|
||||||
// TODO(jmesserly): once we've rolled out `clearState` make it the
|
if (!self || !self.$dartReloadModifiedModules) {
|
||||||
// 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) {
|
|
||||||
console.warn('Hot restart not supported in this environment.');
|
console.warn('Hot restart not supported in this environment.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the application's `onReloadStart()` function, if provided.
|
// If `config.runId` is set (e.g. a unique build ID that represent the
|
||||||
let result;
|
// current build and shared by multiple subapps), skip the following runs
|
||||||
if (_lastLibrary && _lastLibrary.onReloadStart) {
|
// with the same id.
|
||||||
result = _lastLibrary.onReloadStart();
|
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');
|
self.console.clear();
|
||||||
|
const sdk = _libraries.get('dart_sdk');
|
||||||
|
|
||||||
/// Once the `onReloadStart()` completes, this finishes the restart.
|
// Finds out what apps and their subapps should be hot restarted in
|
||||||
function finishHotRestart() {
|
// their starting order.
|
||||||
self.console.clear();
|
const dirtyAppNames = new Array();
|
||||||
if (clearState) {
|
const dirtySubapps = new Array();
|
||||||
// This resets all initialized fields and clears type caches and other
|
if (config && config.runId) {
|
||||||
// temporary data structures used by the compiler/SDK.
|
_nameToApp.forEach(function(app, appName) {
|
||||||
sdk._library.dart.hotRestart();
|
dirtySubapps.push(...app.uuidToSubapp.values());
|
||||||
|
dirtyAppNames.push(appName);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dirtySubapps.push(_lastStartedSubapp);
|
||||||
|
dirtyAppNames.push(_lastStartedSubapp.appName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Reverse the subapps back to starting order.
|
||||||
|
dirtySubapps.reverse();
|
||||||
|
|
||||||
|
await Promise.all(onReloadStartPromises);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starts the subapps in their starting order.
|
||||||
|
for (const subapp of dirtySubapps) {
|
||||||
// Call the module loader to reload the necessary modules.
|
// Call the module loader to reload the necessary modules.
|
||||||
self.$dartWarmReload(() => {
|
self.$dartReloadModifiedModules(subapp.appName, function() {
|
||||||
// Once the modules are loaded, rerun `main()`.
|
// Once the modules are loaded, rerun `main()`.
|
||||||
start(_lastModuleName, _lastLibraryName, true);
|
start(
|
||||||
|
subapp.appName, subapp.uuid, subapp.moduleName,
|
||||||
|
subapp.libraryName, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
dart_library.reload = hotRestart;
|
||||||
|
|
||||||
if (result && result.then) {
|
/// An App contains one or multiple Subapps, all of the subapps share the
|
||||||
result.then(sdk._library.dart.Dynamic)(finishHotRestart);
|
/// same memory copy of library instances, and as a result they share state
|
||||||
} else {
|
/// in Dart statics and top-level fields. There can be one or multiple Apps
|
||||||
finishHotRestart();
|
/// 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dart_library.reload = reload;
|
|
||||||
|
|
||||||
|
class Subapp {
|
||||||
|
constructor(uuid, appName, moduleName, libraryName, library) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.appName = appName;
|
||||||
|
this.moduleName = moduleName;
|
||||||
|
this.libraryName = libraryName;
|
||||||
|
this.library = library;
|
||||||
|
|
||||||
let _lastModuleName;
|
this.originalBody = null;
|
||||||
let _lastLibraryName;
|
}
|
||||||
let _lastLibrary;
|
}
|
||||||
let _originalBody;
|
|
||||||
|
|
||||||
function start(moduleName, libraryName, isReload) {
|
// 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;
|
if (libraryName == null) libraryName = moduleName;
|
||||||
_lastModuleName = moduleName;
|
const library = import_(moduleName, appName)[libraryName];
|
||||||
_lastLibraryName = libraryName;
|
|
||||||
let library = import_(moduleName)[libraryName];
|
let app = _nameToApp.get(appName);
|
||||||
_lastLibrary = library;
|
if (!isReload) {
|
||||||
let dart_sdk = import_('dart_sdk');
|
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) {
|
if (!_debuggerInitialized) {
|
||||||
// This import is only needed for chrome debugging. We should provide an
|
// This import is only needed for chrome debugging. We should provide an
|
||||||
// option to compile without it.
|
// option to compile without it.
|
||||||
dart_sdk._debugger.registerDevtoolsFormatter();
|
sdk._debugger.registerDevtoolsFormatter();
|
||||||
|
|
||||||
// Create isolate.
|
// Create isolate.
|
||||||
_debuggerInitialized = true;
|
_debuggerInitialized = true;
|
||||||
}
|
}
|
||||||
if (isReload) {
|
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) {
|
if (library.onReloadEnd) {
|
||||||
library.onReloadEnd();
|
library.onReloadEnd();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (!!self.document) {
|
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.
|
// 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');
|
self.console.warn('No body saved to update on reload');
|
||||||
} else {
|
} else {
|
||||||
self.document.body = _originalBody;
|
self.document.body = subapp.originalBody;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If not a reload then store the initial html to reset it on reload.
|
// If not a reload and `onReloadEnd` is not defined, store the initial
|
||||||
if (!!self.document && !!self.document.body) {
|
// html to reset it on reload.
|
||||||
_originalBody = self.document.body.cloneNode(true);
|
if (!library.onReloadEnd && !!self.document && !!self.document.body) {
|
||||||
|
subapp.originalBody = self.document.body.cloneNode(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
library.main([]);
|
library.main([]);
|
||||||
|
|
|
@ -350,6 +350,8 @@ class TestDriver {
|
||||||
var bootstrapFile = File(htmlBootstrapper.toFilePath())..createSync();
|
var bootstrapFile = File(htmlBootstrapper.toFilePath())..createSync();
|
||||||
var moduleName = compiler.metadata!.name;
|
var moduleName = compiler.metadata!.name;
|
||||||
var mainLibraryName = compiler.metadataForLibraryUri(input).name;
|
var mainLibraryName = compiler.metadataForLibraryUri(input).name;
|
||||||
|
var appName = p.relative(
|
||||||
|
p.withoutExtension(compiler.metadataForLibraryUri(input).importUri));
|
||||||
|
|
||||||
switch (setup.moduleFormat) {
|
switch (setup.moduleFormat) {
|
||||||
case ModuleFormat.ddc:
|
case ModuleFormat.ddc:
|
||||||
|
@ -368,6 +370,9 @@ class TestDriver {
|
||||||
var dartLibraryPath =
|
var dartLibraryPath =
|
||||||
escaped(p.join(ddcPath, 'lib', 'js', 'legacy', 'dart_library.js'));
|
escaped(p.join(ddcPath, 'lib', 'js', 'legacy', 'dart_library.js'));
|
||||||
var outputPath = output.toFilePath();
|
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('''
|
bootstrapFile.writeAsStringSync('''
|
||||||
<script src='$dartLibraryPath'></script>
|
<script src='$dartLibraryPath'></script>
|
||||||
<script src='$dartSdkPath'></script>
|
<script src='$dartSdkPath'></script>
|
||||||
|
@ -384,7 +389,8 @@ class TestDriver {
|
||||||
sdk.dart.nonNullAsserts(true);
|
sdk.dart.nonNullAsserts(true);
|
||||||
sdk.dart.nativeNonNullAsserts(true);
|
sdk.dart.nativeNonNullAsserts(true);
|
||||||
sdk._debugger.registerDevtoolsFormatter();
|
sdk._debugger.registerDevtoolsFormatter();
|
||||||
dart_library.start('$moduleName', '$mainLibraryName');
|
dart_library.start('$appName', '$uuid', '$moduleName', '$mainLibraryName',
|
||||||
|
false);
|
||||||
</script>
|
</script>
|
||||||
''');
|
''');
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -136,6 +136,14 @@ void main(List<String> args) async {
|
||||||
var libname =
|
var libname =
|
||||||
js_names.pathToJSIdentifier(p.relative(p.withoutExtension(entry)));
|
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
|
// 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
|
// 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
|
// 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
|
// Invoke main through the d8 preamble to ensure the code is running
|
||||||
// within the fake event loop.
|
// within the fake event loop.
|
||||||
self.dartMainRunner(function () {
|
self.dartMainRunner(function () {
|
||||||
dart_library.start("$basename", "$libname");
|
dart_library.start("$appname", "$uuid", "$basename", "$libname", false);
|
||||||
});
|
});
|
||||||
''';
|
''';
|
||||||
var dart2jsD8Preamble =
|
var dart2jsD8Preamble =
|
||||||
|
|
Loading…
Reference in a new issue