[dartdevc] fix #34596, hot restart can now clear field & cache state
This adds a method to the internal SDK runtime library that can be
called to clear state when a hot restart is desired.
This also refactors some of the logic around ignoring type errors,
as we need to ensure we can clear those caches. Also I noticed a bug
where the result of isSubtypeOf was assumed to be non-null, but this
was not the case. The new structure should make this more clear.
(Note: I think we may be able to remove a lot of the code for ignoring
type errors soon.)
Change-Id: Icf5072ffe21aefd85e9816ffc2fc29f679839bc4
Reviewed-on: https://dart-review.googlesource.com/c/78324
Reviewed-by: Jake Macdonald <jakemac@google.com>
Commit-Queue: Jenny Messerly <jmesserly@google.com>
diff --git a/pkg/dev_compiler/lib/js/legacy/dart_library.js b/pkg/dev_compiler/lib/js/legacy/dart_library.js
index 1902d38..8fb0fab 100644
--- a/pkg/dev_compiler/lib/js/legacy/dart_library.js
+++ b/pkg/dev_compiler/lib/js/legacy/dart_library.js
@@ -2,22 +2,23 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-/* This file defines the module loader for the dart runtime.
-*/
+// This file defines the module loader for the dart runtime.
var dart_library;
if (!dart_library) {
-dart_library =
- typeof module != "undefined" && module.exports || {};
+ dart_library = typeof module != "undefined" && module.exports || {};
(function (dart_library) {
'use strict';
- /** Note that we cannot use dart_utils.throwInternalError from here. */
+ // Throws an error related to module loading.
+ //
+ // This does not throw a Dart error because the Dart SDK may not have loaded
+ // yet, and module loading errors cannot be caught by Dart code.
function throwLibraryError(message) {
// Dispatch event to allow others to react to the load error without
// capturing the exception.
- var errorEvent = new CustomEvent('dartLoadException', { detail: message });
- window.dispatchEvent(errorEvent);
+ window.dispatchEvent(
+ new CustomEvent('dartLoadException', { detail: message }));
throw Error(message);
}
@@ -31,12 +32,12 @@
// Returns a proxy that delegates to the underlying loader.
// This defers loading of a module until a library is actually used.
const loadedModule = Symbol('loadedModule');
- dart_library.defer = function(module, name, patch) {
+ dart_library.defer = function (module, name, patch) {
let done = false;
function loadDeferred() {
done = true;
- var mod = module[loadedModule];
- var lib = mod[name];
+ let mod = module[loadedModule];
+ let lib = mod[name];
// Install unproxied module and library in caller's context.
patch(mod, lib);
}
@@ -44,11 +45,11 @@
// library object should be get (to read a top-level variable, method, or
// Class) or set (to write a top-level variable).
return new Proxy({}, {
- get: function(o, p) {
+ get: function (o, p) {
if (!done) loadDeferred();
return module[name][p];
},
- set: function(o, p, value) {
+ set: function (o, p, value) {
if (!done) loadDeferred();
module[name][p] = value;
return true;
@@ -60,8 +61,8 @@
class LibraryLoader {
constructor(name, defaultValue, imports, loader) {
- imports.forEach(function(i) {
- var deps = _reverseImports.get(i);
+ imports.forEach(function (i) {
+ let deps = _reverseImports.get(i);
if (!deps) {
deps = new Set();
_reverseImports.set(i, deps);
@@ -88,8 +89,7 @@
load() {
// Check for cycles
if (this._state == LibraryLoader.LOADING) {
- throwLibraryError('Circular dependence on library: '
- + this._name);
+ throwLibraryError('Circular dependence on library: ' + this._name);
} else if (this._state >= LibraryLoader.READY) {
return this._library;
}
@@ -113,7 +113,7 @@
// Load / parse other modules on demand.
let done = false;
this._library = new Proxy(library, {
- get: function(o, name) {
+ get: function (o, name) {
if (!done) {
done = true;
loader._loader.apply(null, args);
@@ -137,9 +137,9 @@
// Map from name to LibraryLoader
let _libraries = new Map();
- dart_library.libraries = function() { return _libraries.keys(); };
- dart_library.debuggerLibraries = function() {
- var debuggerLibraries = [];
+ dart_library.libraries = function () { return _libraries.keys(); };
+ dart_library.debuggerLibraries = function () {
+ let debuggerLibraries = [];
_libraries.forEach(function (value, key, map) {
debuggerLibraries.push(value.load());
});
@@ -194,37 +194,83 @@
}
dart_library.import = import_;
- var _currentIsolate = false;
+ let _debuggerInitialized = false;
- function _restart() {
- start(_lastModuleName, _lastLibraryName, true);
- }
+ // Called to initiate a hot restart of the application.
+ //
+ // "Hot restart" means all application state is cleared, the newly compiled
+ // modules are loaded, and `main()` is called.
+ //
+ // Note: `onReloadEnd()` can be provided, and if so will be used instead of
+ // `main()` for hot restart.
+ //
+ // This happens in the following sequence:
+ //
+ // 1. Look for `onReloadStart()` in the same library that has `main()`, and
+ // call it if present. This function is implemented by the application to
+ // ensure any global browser/DOM state is cleared, so the application can
+ // restart.
+ // 2. Wait for `onReloadStart()` to complete (either synchronously, or async
+ // if it returned a `Future`).
+ // 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.
+ //
+ function reload(clearState) {
+ // TODO(jmesserly): once we've rolled out `clearState` make it the default,
+ // and eventually remove the parameter.
+ if (clearState == null) clearState = false;
- function reload() {
+
+ // 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 (!window || !window.$dartWarmReload) {
- console.warn('Warm reload not supported in this environment.');
+ console.warn('Hot restart not supported in this environment.');
return;
}
- var result;
+
+ // Call the application's `onReloadStart()` function, if provided.
+ let result;
if (_lastLibrary && _lastLibrary.onReloadStart) {
result = _lastLibrary.onReloadStart();
}
- if (result && result.then) {
- let sdk = _libraries.get("dart_sdk");
- result.then(sdk._library.dart.Dynamic)(function() {
- window.$dartWarmReload(_restart);
+
+ let sdk = _libraries.get("dart_sdk");
+
+ /// Once the `onReloadStart()` completes, this finishes the restart.
+ function finishHotRestart() {
+ if (clearState) {
+ // This resets all initialized fields and clears type caches and other
+ // temporary data structures used by the compiler/SDK.
+ sdk.dart.hotRestart();
+ }
+ // Call the module loader to reload the necessary modules.
+ window.$dartWarmReload(() => {
+ // Once the modules are loaded, rerun `main()`.
+ start(_lastModuleName, _lastLibraryName, true);
});
+ }
+
+ if (result && result.then) {
+ result.then(sdk._library.dart.Dynamic)(finishHotRestart);
} else {
- window.$dartWarmReload(_restart);
+ finishHotRestart();
}
}
dart_library.reload = reload;
- var _lastModuleName;
- var _lastLibraryName;
- var _lastLibrary;
- var _originalBody;
+ let _lastModuleName;
+ let _lastLibraryName;
+ let _lastLibrary;
+ let _originalBody;
function start(moduleName, libraryName, isReload) {
if (libraryName == null) libraryName = moduleName;
@@ -234,14 +280,13 @@
_lastLibrary = library;
let dart_sdk = import_('dart_sdk');
- if (!_currentIsolate) {
+ if (!_debuggerInitialized) {
// This import is only needed for chrome debugging. We should provide an
// option to compile without it.
dart_sdk._debugger.registerDevtoolsFormatter();
// Create isolate.
- _currentIsolate = true;
- dart_sdk._isolate_helper.startRootIsolate(() => {}, []);
+ _debuggerInitialized = true;
}
if (isReload) {
if (library.onReloadEnd) {
diff --git a/pkg/dev_compiler/test/sourcemap/ddc_common.dart b/pkg/dev_compiler/test/sourcemap/ddc_common.dart
index afb62f9..7dbf654 100644
--- a/pkg/dev_compiler/test/sourcemap/ddc_common.dart
+++ b/pkg/dev_compiler/test/sourcemap/ddc_common.dart
@@ -147,10 +147,8 @@
void createHtmlWrapper(File sdkJsFile, Uri outputFile, String jsContent,
String outputFilename, Uri outDir) {
// For debugging via HTML, Chrome and ./tools/testing/dart/http_server.dart.
- Directory sdkPath = sdkRoot;
- String jsRootDart =
- "/root_dart/${new File(path.relative(sdkJsFile.path, from: sdkPath.path))
- .uri}";
+ var sdkFile = File(path.relative(sdkJsFile.path, from: sdkRoot.path));
+ String jsRootDart = "/root_dart/${sdkFile.uri}";
File.fromUri(outputFile.resolve("$outputFilename.html.js")).writeAsStringSync(
jsContent.replaceFirst("from 'dart_sdk'", "from '$jsRootDart'"));
File.fromUri(outputFile.resolve("$outputFilename.html.html"))
@@ -177,7 +175,6 @@
import { test } from '$outFileRootBuild';
let main = test.main;
dart.ignoreWhitelistedErrors(false);
- _isolate_helper.startRootIsolate(() => {}, []);
main();
</script>
</head>
diff --git a/pkg/dev_compiler/tool/ddb b/pkg/dev_compiler/tool/ddb
index 60f32c8..52fe107 100755
--- a/pkg/dev_compiler/tool/ddb
+++ b/pkg/dev_compiler/tool/ddb
@@ -166,7 +166,6 @@
'use strict';
sdk._debugger.registerDevtoolsFormatter();
sdk.dart.ignoreWhitelistedErrors($ignoreWhitelistedErrors);
- sdk._isolate_helper.startRootIsolate(() => {}, []);
app.$basename.main();
});
</script>
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/classes.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/classes.dart
index c8b2e39..3f848d6 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/classes.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/classes.dart
@@ -127,6 +127,7 @@
$throwInternalError('must have at least one generic type argument');
}
let resultMap = new Map();
+ $_cacheMaps.push(resultMap);
function makeGenericType(...args) {
if (args.length != length && args.length != 0) {
$throwInternalError('requires ' + length + ' or 0 type arguments');
@@ -446,7 +447,7 @@
/// Link the extension to the type it's extending as a base class.
setBaseClass(derived, base) {
JS('', '#.prototype.__proto__ = #.prototype', derived, base);
- // We use __proto__ to track the superclass hierarchy (see isSubtype).
+ // We use __proto__ to track the superclass hierarchy (see isSubtypeOf).
JS('', '#.__proto__ = #', derived, base);
}
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart
index 09cf403..46eb700 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart
@@ -390,73 +390,73 @@
dsetindex(obj, index, value) =>
callMethod(obj, '_set', null, [index, value], null, '[]=');
-/// TODO(leafp): This duplicates code in types.dart.
-/// I haven't found a way to factor it out that makes the
-/// code generator happy though.
-_ignoreMemo(f) => JS('', '''(() => {
- let memo = new Map();
- return (t1, t2) => {
- let map = memo.get(t1);
- let result;
- if (map) {
- result = map.get(t2);
- if (result !== void 0) return result;
- } else {
- memo.set(t1, map = new Map());
- }
- result = $f(t1, t2);
- map.set(t2, result);
- return result;
- };
-})()''');
+final _ignoreSubtypeCache = JS('', 'new Map()');
-final Object _ignoreTypeFailure = JS('', '''(() => {
- return $_ignoreMemo((actual, type) => {
- // TODO(vsm): Remove this hack ...
- // This is primarily due to the lack of generic methods,
- // but we need to triage all the types.
- if ($_isFutureOr(type)) {
- // Ignore if we would ignore either side of union.
- let typeArg = $getGenericArgs(type)[0];
- let typeFuture = ${getGenericClass(Future)}(typeArg);
- return $_ignoreTypeFailure(actual, typeFuture) ||
- $_ignoreTypeFailure(actual, typeArg);
- }
+/// Whether [t1] <: [t2], or if [isImplicit] is set and we should ignore the
+/// cast failure from t1 to t2.
+///
+/// See [_isSubtypeOrLegacySubtype] and [ignoreWhitelistedErrors].
+@notNull
+bool _isSubtypeOrIgnorableCastFailure(
+ Object t1, Object t2, @notNull bool isImplicit) {
+ var result = _isSubtypeOrLegacySubtype(t1, t2);
+ return result == true ||
+ result == null &&
+ isImplicit &&
+ JS<bool>('!', 'dart.__ignoreWhitelistedErrors') &&
+ _ignoreTypeFailure(t1, t2);
+}
- if (!!$isSubtype(type, $Iterable) && !!$isSubtype(actual, $Iterable) ||
- !!$isSubtype(type, $Future) && !!$isSubtype(actual, $Future)) {
- console.warn('Ignoring cast fail from ' + $typeName(actual) +
- ' to ' + $typeName(type));
- return true;
+@notNull
+bool _ignoreTypeFailure(Object t1, Object t2) {
+ var map = JS('', '#.get(#)', _ignoreSubtypeCache, t1);
+ if (map != null) {
+ bool result = JS('', '#.get(#)', map, t2);
+ if (JS('!', '# !== void 0', result)) return result;
+ } else {
+ map = JS('', 'new Map()');
+ JS('', '#.set(#, #)', _ignoreSubtypeCache, t1, map);
+ }
+
+ // TODO(vsm): Remove this hack ...
+ // This is primarily due to the lack of generic methods,
+ // but we need to triage all the types.
+ @notNull
+ bool result;
+ if (_isFutureOr(t2)) {
+ // Ignore if we would ignore either side of union.
+ var typeArg = getGenericArgs(t2)[0];
+ var typeFuture = JS('', '#(#)', getGenericClass(Future), typeArg);
+ result =
+ _ignoreTypeFailure(t1, typeFuture) || _ignoreTypeFailure(t1, typeArg);
+ } else {
+ result = t1 is FunctionType && t2 is FunctionType ||
+ isSubtypeOf(t2, unwrapType(Iterable)) &&
+ isSubtypeOf(t1, unwrapType(Iterable)) ||
+ isSubtypeOf(t2, unwrapType(Future)) &&
+ isSubtypeOf(t1, unwrapType(Future));
+ if (result) {
+ _warn('Ignoring cast fail from ${typeName(t1)} to ${typeName(t2)}');
}
- return false;
- });
-})()''');
+ }
+ JS('', '#.set(#, #)', map, t2, result);
+ return result;
+}
@notNull
@JSExportName('is')
bool instanceOf(obj, type) {
if (obj == null) {
- return JS('!', '# == # || #', type, Null, _isTop(type));
+ return identical(type, unwrapType(Null)) || _isTop(type);
}
- return JS('!', '!!#', isSubtype(getReifiedType(obj), type));
+ return isSubtypeOf(getReifiedType(obj), type);
}
@JSExportName('as')
cast(obj, type, @notNull bool isImplicit) {
if (obj == null) return obj;
var actual = getReifiedType(obj);
- var result = isSubtype(actual, type);
- if (JS(
- '!',
- '# === true || # === null && # && '
- 'dart.__ignoreWhitelistedErrors && #(#, #)',
- result,
- result,
- isImplicit,
- _ignoreTypeFailure,
- actual,
- type)) {
+ if (_isSubtypeOrIgnorableCastFailure(actual, type, isImplicit)) {
return obj;
}
return castError(obj, type, isImplicit);
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/rtti.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/rtti.dart
index 3797606..ef023d2 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/rtti.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/rtti.dart
@@ -114,17 +114,17 @@
}
/// Return the module name for a raw library object.
-getModuleName(value) => JS('', '#[#]', value, _moduleName);
+String getModuleName(Object module) => JS('', '#[#]', module, _moduleName);
-var _loadedModules = JS('', 'new Map()');
-var _loadedSourceMaps = JS('', 'new Map()');
+final _loadedModules = JS('', 'new Map()');
+final _loadedSourceMaps = JS('', 'new Map()');
-List getModuleNames() {
- return JS('', 'Array.from(#.keys())', _loadedModules);
+List<String> getModuleNames() {
+ return JSArray<String>.of(JS('', 'Array.from(#.keys())', _loadedModules));
}
-String getSourceMap(module) {
- return JS<String>('!', '#.get(#)', _loadedSourceMaps, module);
+String getSourceMap(String moduleName) {
+ return JS('!', '#.get(#)', _loadedSourceMaps, moduleName);
}
/// Return all library objects in the specified module.
@@ -136,7 +136,7 @@
}
/// Track all libraries
-void trackLibraries(String moduleName, libraries, sourceMap) {
+void trackLibraries(String moduleName, Object libraries, String sourceMap) {
JS('', '#.set(#, #)', _loadedSourceMaps, moduleName, sourceMap);
JS('', '#.set(#, #)', _loadedModules, moduleName, libraries);
}
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/runtime.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/runtime.dart
index 39e38c1..82289ddd 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/runtime.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/runtime.dart
@@ -154,3 +154,31 @@
void setStartAsyncSynchronously([bool value = true]) {
startAsyncSynchronously = value;
}
+
+/// A list of all JS Maps used for caching results, such as by [isSubtypeOf] and
+/// [generic].
+///
+/// This is used by [hotRestart] to ensure we don't leak types from previous
+/// libraries.
+@notNull
+final List<Object> _cacheMaps = JS('!', '[]');
+
+/// A list of functions to reset static fields back to their uninitialized
+/// state.
+///
+/// This is populated by [defineLazyField].
+@notNull
+final List<void Function()> _resetFields = JS('', '[]');
+
+/// Clears out runtime state in `dartdevc` so we can hot-restart.
+///
+/// This should be called when the user requests a hot-restart, when the UI is
+/// handling that user action.
+void hotRestart() {
+ for (var f in _resetFields) f();
+ _resetFields.clear();
+ for (var m in _cacheMaps) JS('', '#.clear()', m);
+ _cacheMaps.clear();
+ JS('', '#.clear()', constantMaps);
+ JS('', '#.clear()', _ignoreSubtypeCache);
+}
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/types.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/types.dart
index c7ab6d8..4602bc2 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/types.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/types.dart
@@ -242,7 +242,7 @@
final _typeObject = JS('', 'Symbol("typeObject")');
/// Given a WrappedType, return the internal runtime type object.
-Object unwrapType(_Type obj) => obj._type;
+Object unwrapType(Type obj) => JS<_Type>('', '#', obj)._type;
// Marker class for generic functions, typedefs, and non-generic functions.
abstract class AbstractFunctionType extends DartType {}
@@ -465,18 +465,11 @@
var actual = JS('', '#[#]', obj, _runtimeType);
// If there's no actual type, it's a JS function.
// Allow them to subtype all Dart function types.
- return JS('!', '# == null || !!#', actual, isSubtype(actual, this));
+ return actual == null || isSubtypeOf(actual, this);
}
return false;
}
- static final void Function(Object, Object) _logIgnoredCast =
- JS('', '''(() => $_ignoreMemo((actual, expected) => {
- console.warn('Ignoring cast fail from ' + $typeName(actual) +
- ' to ' + $typeName(expected));
- return null;
- }))()''');
-
@JSExportName('as')
as_T(obj, [@notNull bool isImplicit = false]) {
if (obj == null) return obj;
@@ -484,13 +477,8 @@
var actual = JS('', '#[#]', obj, _runtimeType);
// If there's no actual type, it's a JS function.
// Allow them to subtype all Dart function types.
- if (actual == null) return obj;
- var result = isSubtype(actual, this);
- if (result == true) return obj;
- if (result == null &&
- isImplicit &&
- JS<bool>('!', 'dart.__ignoreWhitelistedErrors')) {
- _logIgnoredCast(actual, this);
+ if (actual == null ||
+ _isSubtypeOrIgnorableCastFailure(actual, this, isImplicit)) {
return obj;
}
}
@@ -535,7 +523,7 @@
var bounds = instantiateTypeBounds(typeArgs);
var typeFormals = this.typeFormals;
for (var i = 0; i < typeArgs.length; i++) {
- checkTypeBound(typeArgs[i], bounds[i], typeFormals[i]);
+ checkTypeBound(typeArgs[i], bounds[i], typeFormals[i].name);
}
}
@@ -658,7 +646,7 @@
bool is_T(obj) {
if (JS('!', 'typeof # == "function"', obj)) {
var actual = JS('', '#[#]', obj, _runtimeType);
- return JS('!', '# != null && !!#', actual, isSubtype(actual, this));
+ return actual != null && isSubtypeOf(actual, this);
}
return false;
}
@@ -732,12 +720,11 @@
@notNull
bool isType(obj) => JS('', '#[#] === #', obj, _runtimeType, Type);
-void checkTypeBound(type, bound, name) {
- // TODO(jmesserly): we've optimized `is`/`as`/implicit type checks, it would
- // be nice to have similar optimizations for the subtype relation.
- if (JS('!', '#', isSubtype(type, bound))) return;
-
- throwTypeError('type `$type` does not extend `$bound` of `$name`.');
+void checkTypeBound(
+ @notNull Object type, @notNull Object bound, @notNull String name) {
+ if (!isSubtypeOf(type, bound)) {
+ throwTypeError('type `$type` does not extend `$bound` of `$name`.');
+ }
}
@notNull
@@ -793,7 +780,7 @@
for (let i = 0; i < args1.length; ++i) {
if (!$_isSubtype(args2[i], args1[i], !$isCovariant)) {
- // Even if isSubtype returns false, assignability
+ // Even if isSubtypeOf returns false, assignability
// means that we can't be definitive
return null;
}
@@ -845,25 +832,29 @@
return true;
})()''');
+/// Whether [t1] <: [t2].
+@notNull
+bool isSubtypeOf(Object t1, Object t2) {
+ // TODO(jmesserly): we've optimized `is`/`as`/implicit type checks, so they're
+ // dispatched on the type. Can we optimize the subtype relation too?
+ return JS('!', '!!#', _isSubtypeOrLegacySubtype(t1, t2));
+}
+
/// Returns true if [t1] <: [t2].
-/// Returns false if [t1] </: [t2] in both spec and strong mode
-/// Returns undefined if [t1] </: [t2] in strong mode, but spec
-/// mode may differ
-bool isSubtype(t1, t2) {
- // TODO(leafp): This duplicates code in operations.dart.
- // I haven't found a way to factor it out that makes the
- // code generator happy though.
- var map;
- bool result;
+/// Returns false if [t1] </: [t2] and we should not ignore this cast failure.
+/// Returns null if [t1] </: [t2] and we should ignore this cast failure when
+/// the appropriate flags are set.
+bool _isSubtypeOrLegacySubtype(Object t1, Object t2) {
+ Object map;
if (JS('!', '!#.hasOwnProperty(#)', t1, _subtypeCache)) {
JS('', '#[#] = # = new Map()', t1, _subtypeCache, map);
+ _cacheMaps.add(map);
} else {
map = JS('', '#[#]', t1, _subtypeCache);
- result = JS('bool|Null', '#.get(#)', map, t2);
+ bool result = JS('', '#.get(#)', map, t2);
if (JS('!', '# !== void 0', result)) return result;
}
- result =
- JS('bool|Null', '# === # || #(#, #, true)', t1, t2, _isSubtype, t1, t2);
+ var result = _isSubtype(t1, t2, true);
JS('', '#.set(#, #)', map, t2, result);
return result;
}
@@ -882,6 +873,7 @@
type, void_);
}
+@notNull
bool _isFutureOr(type) =>
identical(getGenericClass(type), getGenericClass(FutureOr));
@@ -1399,11 +1391,11 @@
void _constrainLower(Object type) {
if (lower != null) {
- if (isSubtype(lower, type)) {
+ if (isSubtypeOf(lower, type)) {
// nothing to do, existing lower bound is lower than the new one.
return;
}
- if (!isSubtype(type, lower)) {
+ if (!isSubtypeOf(type, lower)) {
// Neither bound is lower and we don't have GLB, so use bottom type.
type = unwrapType(Null);
}
@@ -1413,11 +1405,11 @@
void _constrainUpper(Object type) {
if (upper != null) {
- if (isSubtype(type, upper)) {
+ if (isSubtypeOf(type, upper)) {
// nothing to do, existing upper bound is higher than the new one.
return;
}
- if (!isSubtype(upper, type)) {
+ if (!isSubtypeOf(upper, type)) {
// Neither bound is higher and we don't have LUB, so use top type.
type = unwrapType(Object);
}
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/utils.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/utils.dart
index 3ee1721..dcaf067 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/utils.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/utils.dart
@@ -68,7 +68,8 @@
// TODO(jmesserly): reusing descriptor objects has been shown to improve
// performance in other projects (e.g. webcomponents.js ShadowDOM polyfill).
defineLazyField(to, name, desc) => JS('', '''(() => {
- let init = $desc.get;
+ const initializer = $desc.get;
+ let init = initializer;
let value = null;
$desc.get = function() {
if (init == null) return value;
@@ -92,6 +93,10 @@
value = x;
};
}
+ $_resetFields.push(() => {
+ init = initializer;
+ value = null;
+ });
return ${defineProperty(to, name, desc)};
})()''');
diff --git a/pkg/dev_compiler/tool/input_sdk/private/debugger.dart b/pkg/dev_compiler/tool/input_sdk/private/debugger.dart
index 86592dc..6ee554b 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/debugger.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/debugger.dart
@@ -967,14 +967,14 @@
JS('', '#.devtoolsFormatters = [#]', dart.global_, _devtoolsFormatter);
}
-// Expose these methods here to facilitate writing debugger tests.
-// If export worked for private SDK libraries we could just export
-// these methods from dart:_runtime.
-
-getModuleNames() {
- return dart.getModuleNames();
-}
-
-getModuleLibraries(String name) {
- return dart.getModuleLibraries(name);
-}
+// These methods are exposed here for debugger tests.
+//
+// TODO(jmesserly): these are not exports because there is existing code that
+// calls into them from JS. Currently `dartdevc` always resolves exports at
+// compile time, so there is no need to make exports available at runtime by
+// copying properties. For that reason we cannot use re-export.
+//
+// If these methods are only for tests, we should move them here, or change the
+// tests to call the methods directly on dart:_runtime.
+List<String> getModuleNames() => dart.getModuleNames();
+getModuleLibraries(String name) => dart.getModuleLibraries(name);