[ddc] Reset all initialized consts on hot restart

Clear local caches storing const values in each module.

Change-Id: I7766e92df6b8d1f91bad5fbb2addb8ace7763646
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/347220
Reviewed-by: Mark Zhou <markzipan@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
This commit is contained in:
Nicholas Shahan 2024-01-22 23:25:18 +00:00 committed by Commit Queue
parent 39b7249915
commit b811ab5b04
3 changed files with 44 additions and 12 deletions

View file

@ -519,8 +519,15 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
moduleItems.addAll(afterClassDefItems);
afterClassDefItems.clear();
// Register the local const cache for this module so it can be cleared on a
// hot restart.
if (_constTableCache.isNotEmpty) {
moduleItems.add(runtimeCall('moduleConstCaches.set(#, #)', [
js_ast.string(_options.moduleName),
_constTableCache.containerId
]).toStatement());
}
_ticker?.logMs('Added table caches');
// Add all type hierarchy rules for the interface types used in this module.
if (_options.newRuntimeTypes) {
// TODO(nshahan) This is likely more information than the application

View file

@ -236,6 +236,13 @@ final List<Object> _cacheMaps = JS('!', '[]');
@notNull
final List<void Function()> resetFields = JS('', '[]');
/// A map of module ids (names) to the local const value cache in that module.
///
/// This is populated on module load and each cache is cleared during
/// [hotRestart].
@notNull
final JSArray<Object?> moduleConstCaches = JS('!', 'new Map()');
/// A counter to track each time [hotRestart] is invoked. This is used to ensure
/// that pending callbacks that were created on a previous iteration (e.g. a
/// timer callback or a DOM callback) will not execute when they get invoked.
@ -254,8 +261,16 @@ void hotRestart() {
resetFields.clear();
for (var m in _cacheMaps) JS('', '#.clear()', m);
_cacheMaps.clear();
// TODO(nshahan) Verify _nullComparisonSet isn't used with the new type system
// and delete.
JS('', '#.clear()', _nullComparisonSet);
JS('', '#.clear()', constants);
JS('', '#.clear()', constantLists);
JS('', '#.clear()', constantSets);
JS('', '#.clear()', constantMaps);
JS('', '#.forEach((value) => value.fill(void 0))', moduleConstCaches);
if (!_ddcDeferredLoading) {
JS('', '#.clear()', deferredImports);
}

View file

@ -27,13 +27,22 @@ main() {
// Read this static field first to avoid interference with other reset counts.
var weakNullSafety = hasUnsoundNullSafety;
// TODO(42495) `Expect.throws` contains the use of a const that triggers an
// extra field reset but consts are not correctly reset on hot restart so the
// extra reset only appears once. Perform an initial Expect.throws here to
// avoid confusion with the reset counts later.
// The first call of `Expect.throws` involves type tests that initialize
// values in the runtime type system that will read static fields. After
// initialization fields are never read again (even after a hot restart)
// so we perform the first call here before we start counting field resets.
Expect.throws(() => throw 'foo');
var resetFieldCount = dart.resetFields.length;
dart.hotRestart();
// Count the number of reset fields triggered by a call to `Expect.throws`
// after every hot restart. This value is used as an offset in expectations
// below.
Expect.throws(() => throw 'foo');
var expectThrowsResetFieldCount = dart.resetFields.length;
Expect.equals(1, expectThrowsResetFieldCount);
dart.hotRestart();
// Set uninitialized static late fields. Avoid calling getters for these
// statics to ensure they are reset even if they are never accessed.
@ -62,12 +71,10 @@ main() {
// Sound Null Safety - 6 total field resets:
// - 3 write/resets for the actual uninitialized field writes.
// - 3 reads/resets for the actual initialized field reads.
var expectedResets =
weakNullSafety ? resetFieldCount + 12 : resetFieldCount + 6;
var expectedResets = weakNullSafety ? 12 : 6;
Expect.equals(expectedResets, dart.resetFields.length);
dart.hotRestart();
resetFieldCount = dart.resetFields.length;
// Late statics should throw on get when not initialized.
Expect.throws(() => noInitializer);
@ -95,12 +102,13 @@ main() {
// Sound Null Safety - 6 total field resets:
// - 3 write/resets for the actual uninitialized field writes.
// - 3 reads/resets for the actual initialized field reads.
expectedResets = weakNullSafety ? resetFieldCount + 12 : resetFieldCount + 6;
expectedResets = weakNullSafety
? expectThrowsResetFieldCount + 12
: expectThrowsResetFieldCount + 6;
Expect.equals(expectedResets, dart.resetFields.length);
dart.hotRestart();
dart.hotRestart();
resetFieldCount = dart.resetFields.length;
// Late statics should throw on get when not initialized.
Expect.throws(() => noInitializer);
@ -120,6 +128,8 @@ main() {
// Sound Null Safety - 6 total field resets:
// - 3 reads/resets for actual uninitialized field reads.
// - 3 reads/resets for the actual initialized field reads.
expectedResets = weakNullSafety ? resetFieldCount + 9 : resetFieldCount + 6;
expectedResets = weakNullSafety
? expectThrowsResetFieldCount + 9
: expectThrowsResetFieldCount + 6;
Expect.equals(expectedResets, dart.resetFields.length);
}