[ddc] Apply late semantics to statics with initializers
Ensures that static finals with recursive initializers will throw
a `LateInitializationError` after trying to assign the value twice,
even if the value is the same.
Change-Id: Iae85a4fab93f51161a9842a684b0efd2aeba9dbc
Fixes: https://github.com/dart-lang/sdk/issues/43358
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/162280
Reviewed-by: Mark Zhou <markzipan@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
index 6b33186..13ab061 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
@@ -43,6 +43,10 @@
}
}
+throwLateInitializationError(String name) {
+ throw internal.LateInitializationErrorImpl(name);
+}
+
throwCyclicInitializationError([String? field]) {
throw CyclicInitializationError(field);
}
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
index 489795f..d931293 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
@@ -785,22 +785,38 @@
// performance in other projects (e.g. webcomponents.js ShadowDOM polyfill).
defineLazyField(to, name, desc) => JS('', '''(() => {
const initializer = $desc.get;
+ const final = $desc.set == null;
+ // Tracks if the initializer has been called.
+ let initialized = false;
let init = initializer;
let value = null;
- let executed = false;
+ // Tracks if these local variables have been saved so they can be restored
+ // after a hot restart.
+ let savedLocals = false;
$desc.get = function() {
if (init == null) return value;
- if (!executed) {
+ if (final && initialized) $throwLateInitializationError($name);
+ if (!savedLocals) {
// Record the field on first execution so we can reset it later if
// needed (hot restart).
$_resetFields.push(() => {
init = initializer;
value = null;
- executed = false;
+ savedLocals = false;
+ initialized = false;
});
- executed = true;
+ savedLocals = true;
}
- value = init();
+ // Must set before calling init in case it is recursive.
+ initialized = true;
+ try {
+ value = init();
+ } catch (e) {
+ // Reset to false so the initializer can be executed again if the
+ // exception was caught.
+ initialized = false;
+ throw e;
+ }
init = null;
return value;
};
@@ -809,7 +825,7 @@
$desc.set = function(x) {
init = null;
value = x;
- // executed is dead since init is set to null
+ // savedLocals and initialized are dead since init is set to null
};
}
return ${defineProperty(to, name, desc)};
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 f07c4d8..cb832e0 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
@@ -11,7 +11,7 @@
import 'dart:_debugger' show stackTraceMapper, trackCall;
import 'dart:_foreign_helper' show JS, JSExportName, rest, spread;
import 'dart:_interceptors' show JSArray, jsNull, JSFunction, NativeError;
-import 'dart:_internal' as internal show Symbol;
+import 'dart:_internal' as internal show LateInitializationErrorImpl, Symbol;
import 'dart:_js_helper'
show
AssertionErrorImpl,