Skip to content

Commit e20ff1e

Browse files
liamappelbecommit-bot@chromium.org
authored andcommitted
[vm] Prevent late fields from being unboxed
When unboxed doubles are assigned to late fields, it conflicts with the late field logic and causes a crash. Bug: #38841 Bug: #39658 Change-Id: I641f597006114f02473a20f8c2526eeaf4fe813f Fixes: #39658 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/127442 Commit-Queue: Liam Appelbe <liama@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
1 parent 7ac3f3e commit e20ff1e

File tree

8 files changed

+112
-35
lines changed

8 files changed

+112
-35
lines changed

runtime/vm/compiler/backend/slot_test.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ TEST_CASE(SlotFromGuardedField) {
5454
const Field& field = Field::Handle(
5555
Field::New(String::Handle(Symbols::New(thread, "field")),
5656
/*is_static=*/false, /*is_final=*/false, /*is_const=*/false,
57-
/*is_reflectable=*/true, dummy_class, Object::dynamic_type(),
58-
TokenPosition::kMinSource, TokenPosition::kMinSource));
57+
/*is_reflectable=*/true, /*is_late=*/false, dummy_class,
58+
Object::dynamic_type(), TokenPosition::kMinSource,
59+
TokenPosition::kMinSource));
5960

6061
// Set non-trivial guarded state on the field.
6162
field.set_guarded_cid(kSmiCid);

runtime/vm/compiler/backend/type_propagator_test.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ ISOLATE_UNIT_TEST_CASE(TypePropagator_Refinement) {
184184
/*is_static=*/true,
185185
/*is_final=*/false,
186186
/*is_const=*/false,
187-
/*is_reflectable=*/true, object_class, Object::dynamic_type(),
187+
/*is_reflectable=*/true,
188+
/*is_late=*/false, object_class, Object::dynamic_type(),
188189
TokenPosition::kNoSource, TokenPosition::kNoSource));
189190

190191
FlowGraphBuilderHelper H;

runtime/vm/compiler/frontend/bytecode_reader.cc

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,16 +2017,15 @@ void BytecodeReaderHelper::ReadFieldDeclarations(const Class& cls,
20172017
}
20182018

20192019
field = Field::New(name, is_static, is_final, is_const,
2020-
(flags & kIsReflectableFlag) != 0, script_class, type,
2021-
position, end_position);
2020+
(flags & kIsReflectableFlag) != 0, is_late, script_class,
2021+
type, position, end_position);
20222022

20232023
field.set_is_declared_in_bytecode(true);
20242024
field.set_has_pragma(has_pragma);
20252025
field.set_is_covariant((flags & kIsCovariantFlag) != 0);
20262026
field.set_is_generic_covariant_impl((flags & kIsGenericCovariantImplFlag) !=
20272027
0);
20282028
field.set_has_nontrivial_initializer(has_nontrivial_initializer);
2029-
field.set_is_late((flags & kIsLateFlag) != 0);
20302029
field.set_is_extension_member(is_extension_member);
20312030
field.set_has_initializer(has_initializer);
20322031

@@ -2134,13 +2133,13 @@ void BytecodeReaderHelper::ReadFieldDeclarations(const Class& cls,
21342133

21352134
if (cls.is_enum_class()) {
21362135
// Add static field 'const _deleted_enum_sentinel'.
2137-
field =
2138-
Field::New(Symbols::_DeletedEnumSentinel(),
2139-
/* is_static = */ true,
2140-
/* is_final = */ true,
2141-
/* is_const = */ true,
2142-
/* is_reflectable = */ false, cls, Object::dynamic_type(),
2143-
TokenPosition::kNoSource, TokenPosition::kNoSource);
2136+
field = Field::New(Symbols::_DeletedEnumSentinel(),
2137+
/* is_static = */ true,
2138+
/* is_final = */ true,
2139+
/* is_const = */ true,
2140+
/* is_reflectable = */ false,
2141+
/* is_late = */ false, cls, Object::dynamic_type(),
2142+
TokenPosition::kNoSource, TokenPosition::kNoSource);
21442143

21452144
fields.SetAt(num_fields, field);
21462145
}

runtime/vm/kernel_loader.cc

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,12 +1187,11 @@ void KernelLoader::FinishTopLevelClassLoading(
11871187
const bool is_late = field_helper.IsLate();
11881188
const bool is_extension_member = field_helper.IsExtensionMember();
11891189
const Field& field = Field::Handle(
1190-
Z,
1191-
Field::NewTopLevel(name, is_final, field_helper.IsConst(), script_class,
1192-
field_helper.position_, field_helper.end_position_));
1190+
Z, Field::NewTopLevel(name, is_final, field_helper.IsConst(), is_late,
1191+
script_class, field_helper.position_,
1192+
field_helper.end_position_));
11931193
field.set_kernel_offset(field_offset);
11941194
field.set_has_pragma(has_pragma_annotation);
1195-
field.set_is_late(is_late);
11961195
field.set_is_extension_member(is_extension_member);
11971196
const AbstractType& type = T.BuildType(); // read type.
11981197
field.SetFieldType(type);
@@ -1541,16 +1540,15 @@ void KernelLoader::FinishClassLoading(const Class& klass,
15411540
const bool is_late = field_helper.IsLate();
15421541
const bool is_extension_member = field_helper.IsExtensionMember();
15431542
Field& field = Field::Handle(
1544-
Z,
1545-
Field::New(name, field_helper.IsStatic(), is_final,
1546-
field_helper.IsConst(), is_reflectable, script_class, type,
1547-
field_helper.position_, field_helper.end_position_));
1543+
Z, Field::New(name, field_helper.IsStatic(), is_final,
1544+
field_helper.IsConst(), is_reflectable, is_late,
1545+
script_class, type, field_helper.position_,
1546+
field_helper.end_position_));
15481547
field.set_kernel_offset(field_offset);
15491548
field.set_has_pragma(has_pragma_annotation);
15501549
field.set_is_covariant(field_helper.IsCovariant());
15511550
field.set_is_generic_covariant_impl(
15521551
field_helper.IsGenericCovariantImpl());
1553-
field.set_is_late(is_late);
15541552
field.set_is_extension_member(is_extension_member);
15551553
ReadInferredType(field, field_offset + library_kernel_offset_);
15561554
CheckForInitializer(field);
@@ -1575,13 +1573,14 @@ void KernelLoader::FinishClassLoading(const Class& klass,
15751573
// Add static field 'const _deleted_enum_sentinel'.
15761574
// This field does not need to be of type E.
15771575
Field& deleted_enum_sentinel = Field::ZoneHandle(Z);
1578-
deleted_enum_sentinel = Field::New(
1579-
Symbols::_DeletedEnumSentinel(),
1580-
/* is_static = */ true,
1581-
/* is_final = */ true,
1582-
/* is_const = */ true,
1583-
/* is_reflectable = */ false, klass, Object::dynamic_type(),
1584-
TokenPosition::kNoSource, TokenPosition::kNoSource);
1576+
deleted_enum_sentinel =
1577+
Field::New(Symbols::_DeletedEnumSentinel(),
1578+
/* is_static = */ true,
1579+
/* is_final = */ true,
1580+
/* is_const = */ true,
1581+
/* is_reflectable = */ false,
1582+
/* is_late = */ false, klass, Object::dynamic_type(),
1583+
TokenPosition::kNoSource, TokenPosition::kNoSource);
15851584
fields_.Add(&deleted_enum_sentinel);
15861585
}
15871586

runtime/vm/object.cc

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3834,7 +3834,11 @@ bool Class::InjectCIDFields() const {
38343834
const AbstractType& field_type = Type::Handle(zone, Type::IntType());
38353835
for (size_t i = 0; i < ARRAY_SIZE(cid_fields); i++) {
38363836
field_name = Symbols::New(thread, cid_fields[i].field_name);
3837-
field = Field::New(field_name, true, false, true, false, *this, field_type,
3837+
field = Field::New(field_name, /* is_static = */ true,
3838+
/* is_final = */ false,
3839+
/* is_const = */ true,
3840+
/* is_reflectable = */ false,
3841+
/* is_late = */ false, *this, field_type,
38383842
TokenPosition::kMinSource, TokenPosition::kMinSource);
38393843
value = Smi::New(cid_fields[i].cid);
38403844
field.SetStaticValue(value, true);
@@ -8746,6 +8750,7 @@ void Field::InitializeNew(const Field& result,
87468750
bool is_final,
87478751
bool is_const,
87488752
bool is_reflectable,
8753+
bool is_late,
87498754
const Object& owner,
87508755
TokenPosition token_pos,
87518756
TokenPosition end_token_pos) {
@@ -8758,13 +8763,14 @@ void Field::InitializeNew(const Field& result,
87588763
result.set_is_final(is_final);
87598764
result.set_is_const(is_const);
87608765
result.set_is_reflectable(is_reflectable);
8766+
result.set_is_late(is_late);
87618767
result.set_is_double_initialized(false);
87628768
result.set_owner(owner);
87638769
result.set_token_pos(token_pos);
87648770
result.set_end_token_pos(end_token_pos);
87658771
result.set_has_nontrivial_initializer(false);
87668772
result.set_has_initializer(false);
8767-
result.set_is_unboxing_candidate(!is_final);
8773+
result.set_is_unboxing_candidate(!is_final && !is_late);
87688774
result.set_initializer_changed_after_initialization(false);
87698775
NOT_IN_PRECOMPILED(result.set_is_declared_in_bytecode(false));
87708776
NOT_IN_PRECOMPILED(result.set_binary_declaration_offset(0));
@@ -8800,29 +8806,31 @@ RawField* Field::New(const String& name,
88008806
bool is_final,
88018807
bool is_const,
88028808
bool is_reflectable,
8809+
bool is_late,
88038810
const Object& owner,
88048811
const AbstractType& type,
88058812
TokenPosition token_pos,
88068813
TokenPosition end_token_pos) {
88078814
ASSERT(!owner.IsNull());
88088815
const Field& result = Field::Handle(Field::New());
88098816
InitializeNew(result, name, is_static, is_final, is_const, is_reflectable,
8810-
owner, token_pos, end_token_pos);
8817+
is_late, owner, token_pos, end_token_pos);
88118818
result.SetFieldType(type);
88128819
return result.raw();
88138820
}
88148821

88158822
RawField* Field::NewTopLevel(const String& name,
88168823
bool is_final,
88178824
bool is_const,
8825+
bool is_late,
88188826
const Object& owner,
88198827
TokenPosition token_pos,
88208828
TokenPosition end_token_pos) {
88218829
ASSERT(!owner.IsNull());
88228830
const Field& result = Field::Handle(Field::New());
88238831
InitializeNew(result, name, true, /* is_static */
88248832
is_final, is_const, true, /* is_reflectable */
8825-
owner, token_pos, end_token_pos);
8833+
is_late, owner, token_pos, end_token_pos);
88268834
return result.raw();
88278835
}
88288836

@@ -10240,6 +10248,7 @@ void Library::AddMetadata(const Object& owner,
1024010248
Field::Handle(zone, Field::NewTopLevel(metaname,
1024110249
false, // is_final
1024210250
false, // is_const
10251+
false, // is_late
1024310252
owner, token_pos, token_pos));
1024410253
field.SetFieldType(Object::dynamic_type());
1024510254
field.set_is_reflectable(false);
@@ -12002,6 +12011,7 @@ void Namespace::AddMetadata(const Object& owner,
1200212011
Field& field = Field::Handle(Field::NewTopLevel(Symbols::TopLevel(),
1200312012
false, // is_final
1200412013
false, // is_const
12014+
false, // is_late
1200512015
owner, token_pos, token_pos));
1200612016
field.set_is_reflectable(false);
1200712017
field.SetFieldType(Object::dynamic_type());

runtime/vm/object.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3603,6 +3603,7 @@ class Field : public Object {
36033603
bool is_final,
36043604
bool is_const,
36053605
bool is_reflectable,
3606+
bool is_late,
36063607
const Object& owner,
36073608
const AbstractType& type,
36083609
TokenPosition token_pos,
@@ -3611,6 +3612,7 @@ class Field : public Object {
36113612
static RawField* NewTopLevel(const String& name,
36123613
bool is_final,
36133614
bool is_const,
3615+
bool is_late,
36143616
const Object& owner,
36153617
TokenPosition token_pos,
36163618
TokenPosition end_token_pos);
@@ -3854,6 +3856,7 @@ class Field : public Object {
38543856
bool is_final,
38553857
bool is_const,
38563858
bool is_reflectable,
3859+
bool is_late,
38573860
const Object& owner,
38583861
TokenPosition token_pos,
38593862
TokenPosition end_token_pos);

runtime/vm/object_test.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ ISOLATE_UNIT_TEST_CASE(InstanceClass) {
234234
const Array& one_fields = Array::Handle(Array::New(1));
235235
const String& field_name = String::Handle(Symbols::New(thread, "the_field"));
236236
const Field& field = Field::Handle(
237-
Field::New(field_name, false, false, false, true, one_field_class,
237+
Field::New(field_name, false, false, false, true, false, one_field_class,
238238
Object::dynamic_type(), TokenPosition::kMinSource,
239239
TokenPosition::kMinSource));
240240
one_fields.SetAt(0, field);
@@ -2852,7 +2852,7 @@ static RawField* CreateTestField(const char* name) {
28522852
const String& field_name =
28532853
String::Handle(Symbols::New(Thread::Current(), name));
28542854
const Field& field = Field::Handle(Field::New(
2855-
field_name, true, false, false, true, cls, Object::dynamic_type(),
2855+
field_name, true, false, false, true, false, cls, Object::dynamic_type(),
28562856
TokenPosition::kMinSource, TokenPosition::kMinSource));
28572857
return field.raw();
28582858
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// SharedOptions=--enable-experiment=non-nullable
6+
import 'package:expect/expect.dart';
7+
8+
int initCalls = 0;
9+
double init() {
10+
++initCalls;
11+
return 1.23;
12+
}
13+
14+
class A {
15+
late double? fieldWithInit = init();
16+
late double fieldWithTrivialInit = 1.23;
17+
late double? fieldWithNullInit = null;
18+
late double fieldWithNoInit;
19+
}
20+
21+
main() {
22+
// Late, non-final, with init.
23+
var a = A();
24+
Expect.equals(0, initCalls);
25+
Expect.equals(1.23, a.fieldWithInit);
26+
Expect.equals(1.23, a.fieldWithTrivialInit);
27+
Expect.equals(null, a.fieldWithNullInit);
28+
Expect.throws(
29+
() => a.fieldWithNoInit, (error) => error is LateInitializationError);
30+
Expect.equals(1, initCalls);
31+
Expect.equals(1.23, a.fieldWithInit);
32+
Expect.equals(1.23, a.fieldWithTrivialInit);
33+
Expect.equals(null, a.fieldWithNullInit);
34+
Expect.throws(
35+
() => a.fieldWithNoInit, (error) => error is LateInitializationError);
36+
Expect.equals(1, initCalls);
37+
a.fieldWithInit = 4.56;
38+
a.fieldWithTrivialInit = 4.56;
39+
a.fieldWithNullInit = 4.56;
40+
a.fieldWithNoInit = 4.56;
41+
Expect.equals(1, initCalls);
42+
Expect.equals(4.56, a.fieldWithInit);
43+
Expect.equals(4.56, a.fieldWithTrivialInit);
44+
Expect.equals(4.56, a.fieldWithNullInit);
45+
Expect.equals(4.56, a.fieldWithNoInit);
46+
Expect.equals(1, initCalls);
47+
initCalls = 0;
48+
49+
// Late, non-final, with init that's pre-empted by setter.
50+
var b = A();
51+
Expect.equals(0, initCalls);
52+
b.fieldWithInit = 4.56;
53+
Expect.equals(0, initCalls);
54+
Expect.equals(4.56, b.fieldWithInit);
55+
Expect.equals(0, initCalls);
56+
57+
// Late, non-final, with init that's pre-empted by null setter.
58+
var c = A();
59+
Expect.equals(0, initCalls);
60+
c.fieldWithInit = null;
61+
Expect.equals(0, initCalls);
62+
Expect.equals(null, c.fieldWithInit);
63+
Expect.equals(0, initCalls);
64+
}

0 commit comments

Comments
 (0)