[vm] Fix materialization of instances with uninitialized late fields
When materializing objects during deoptimization, their fields were
set using Instance::SetField. However, Instance::SetField updates
field guards and it is strict about not taking 'sentinel' value.
That caused assertion to fail when trying to materialize an instances
with late fields which were not initialized yet and have 'sentinel'
value.
Changed materialization to use a more low-level SetFieldAtOffset
in case of sentinel value.
TEST=runtime/tests/vm/dart/regress_45691_test.dart
Fixes https://github.com/dart-lang/sdk/issues/45691
Change-Id: If39a1e4f2fc0d0fe7f988a80c7a0073cafff28b0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/195490
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/runtime/tests/vm/dart/regress_45691_test.dart b/runtime/tests/vm/dart/regress_45691_test.dart
new file mode 100644
index 0000000..f1c3d18
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45691_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45691.
+// Verifies that materialization of objects with uninitialized late fields
+// doesn't crash.
+
+// VMOptions=--optimization-counter-threshold=100 --deterministic
+
+import 'package:expect/expect.dart';
+
+class A {
+ late int x = int.parse('42');
+ int y = 10;
+}
+
+@pragma("vm:never-inline")
+void foo(num deopt) {
+ A a = A();
+ deopt + 1;
+ Expect.equals(10, a.y);
+}
+
+void main() {
+ for (int i = 0; i < 150; ++i) {
+ foo(i > 100 ? 1.0 : 2);
+ }
+}
diff --git a/runtime/vm/deferred_objects.cc b/runtime/vm/deferred_objects.cc
index 27edd7a..8fa9865 100644
--- a/runtime/vm/deferred_objects.cc
+++ b/runtime/vm/deferred_objects.cc
@@ -440,7 +440,9 @@
offset ^= GetFieldOffset(i);
field ^= offset_map.At(offset.Value() / kWordSize);
value = GetValue(i);
- if (!field.IsNull()) {
+ ASSERT((value.ptr() != Object::sentinel().ptr()) ||
+ (!field.IsNull() && field.is_late()));
+ if (!field.IsNull() && (value.ptr() != Object::sentinel().ptr())) {
obj.SetField(field, value);
if (FLAG_trace_deoptimization_verbose) {
OS::PrintErr(" %s <- %s\n",
@@ -455,8 +457,11 @@
ASSERT(offset.Value() < cls.host_instance_size());
obj.SetFieldAtOffset(offset.Value(), value);
if (FLAG_trace_deoptimization_verbose) {
- OS::PrintErr(" null Field @ offset(%" Pd ") <- %s\n",
- offset.Value(), value.ToCString());
+ OS::PrintErr(
+ " %s @ offset(%" Pd ") <- %s\n",
+ (field.IsNull() ? "null Field"
+ : String::Handle(field.name()).ToCString()),
+ offset.Value(), value.ToCString());
}
}
}