diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart index 5549aeda4ea..f69d0f6bb8b 100644 --- a/pkg/dev_compiler/lib/src/kernel/compiler.dart +++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart @@ -519,8 +519,15 @@ class ProgramCompiler extends ComputeOnceConstantVisitor 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 diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart index 2ff032d6e3c..2e01104f19b 100644 --- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart +++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart @@ -236,6 +236,13 @@ final List _cacheMaps = JS('!', '[]'); @notNull final List 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 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); } diff --git a/tests/dartdevc/hot_restart_late_test.dart b/tests/dartdevc/hot_restart_late_test.dart index 32a31d7295b..496f21d0183 100644 --- a/tests/dartdevc/hot_restart_late_test.dart +++ b/tests/dartdevc/hot_restart_late_test.dart @@ -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); }