[vm] Account for named record fields which can be named like positional

It is allowed to have named fields $N in records unless they conflict
with corresponding positional fields. So when accessing $N fields
dynamically we should look for both positional field and named field.

TEST=language/records/simple/dynamic_field_access_test

Issue: https://github.com/dart-lang/sdk/issues/49719
Change-Id: Id31dcb82e753aeeaeed74d5d07aac556ae08a7b2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/264740
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2022-10-18 21:03:09 +00:00 committed by Commit Queue
parent e3f77e928f
commit d7852e4786
3 changed files with 82 additions and 76 deletions

View file

@ -2291,7 +2291,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecordFieldGetter(
const intptr_t field_index =
Record::GetPositionalFieldIndexFromFieldName(name);
if (field_index >= 0) {
// Get record field by index.
// Get positional record field by index.
body += IntConstant(field_index);
// num_positional = num_fields - field_names.length
@ -2307,86 +2307,87 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecordFieldGetter(
TargetEntryInstr* valid_index;
TargetEntryInstr* invalid_index;
body += BranchIfTrue(&valid_index, &invalid_index);
Fragment(invalid_index) + Goto(nsm);
body.current = valid_index;
body += LoadLocal(parsed_function_->receiver_var());
body += LoadNativeField(Slot::GetRecordFieldSlot(
thread_, compiler::target::Record::field_offset(field_index)));
body += Return(TokenPosition::kNoSource);
} else {
// Search field among named fields.
body += LoadLocal(parsed_function_->receiver_var());
body += LoadNativeField(Slot::Record_field_names());
LocalVariable* field_names = MakeTemporary("field_names");
body += LoadLocal(field_names);
body += LoadNativeField(Slot::Array_length());
LocalVariable* num_named = MakeTemporary("num_named");
body += IntConstant(0);
body += LoadLocal(num_named);
body += SmiRelationalOp(Token::kLT);
TargetEntryInstr* has_named_fields;
TargetEntryInstr* no_named_fields;
body += BranchIfTrue(&has_named_fields, &no_named_fields);
Fragment(no_named_fields) + Goto(nsm);
body.current = has_named_fields;
LocalVariable* index = parsed_function_->expression_temp_var();
body += IntConstant(0);
body += StoreLocal(TokenPosition::kNoSource, index);
body += Drop();
JoinEntryInstr* loop = BuildJoinEntry();
body += Goto(loop);
body.current = loop;
body += LoadLocal(field_names);
body += LoadLocal(index);
body += LoadIndexed(kArrayCid,
/*index_scale*/ compiler::target::kCompressedWordSize);
body += Constant(name);
TargetEntryInstr* found;
TargetEntryInstr* continue_search;
body += BranchIfEqual(&found, &continue_search);
body.current = continue_search;
body += LoadLocal(index);
body += IntConstant(1);
body += SmiBinaryOp(Token::kADD);
body += StoreLocal(TokenPosition::kNoSource, index);
body += Drop();
body += LoadLocal(index);
body += LoadLocal(num_named);
body += SmiRelationalOp(Token::kLT);
TargetEntryInstr* has_more_fields;
TargetEntryInstr* no_more_fields;
body += BranchIfTrue(&has_more_fields, &no_more_fields);
Fragment(has_more_fields) + Goto(loop);
Fragment(no_more_fields) + Goto(nsm);
body.current = found;
body += LoadLocal(parsed_function_->receiver_var());
body += LoadLocal(parsed_function_->receiver_var());
body += LoadNativeField(Slot::Record_num_fields());
body += Box(Slot::Record_num_fields().representation());
body += LoadLocal(num_named);
body += SmiBinaryOp(Token::kSUB);
body += LoadLocal(index);
body += SmiBinaryOp(Token::kADD);
body += LoadIndexed(kRecordCid,
/*index_scale*/ compiler::target::kCompressedWordSize);
body += DropTempsPreserveTop(2);
body += Return(TokenPosition::kNoSource);
body.current = invalid_index;
}
// Search field among named fields.
body += LoadLocal(parsed_function_->receiver_var());
body += LoadNativeField(Slot::Record_field_names());
LocalVariable* field_names = MakeTemporary("field_names");
body += LoadLocal(field_names);
body += LoadNativeField(Slot::Array_length());
LocalVariable* num_named = MakeTemporary("num_named");
body += IntConstant(0);
body += LoadLocal(num_named);
body += SmiRelationalOp(Token::kLT);
TargetEntryInstr* has_named_fields;
TargetEntryInstr* no_named_fields;
body += BranchIfTrue(&has_named_fields, &no_named_fields);
Fragment(no_named_fields) + Goto(nsm);
body.current = has_named_fields;
LocalVariable* index = parsed_function_->expression_temp_var();
body += IntConstant(0);
body += StoreLocal(TokenPosition::kNoSource, index);
body += Drop();
JoinEntryInstr* loop = BuildJoinEntry();
body += Goto(loop);
body.current = loop;
body += LoadLocal(field_names);
body += LoadLocal(index);
body += LoadIndexed(kArrayCid,
/*index_scale*/ compiler::target::kCompressedWordSize);
body += Constant(name);
TargetEntryInstr* found;
TargetEntryInstr* continue_search;
body += BranchIfEqual(&found, &continue_search);
body.current = continue_search;
body += LoadLocal(index);
body += IntConstant(1);
body += SmiBinaryOp(Token::kADD);
body += StoreLocal(TokenPosition::kNoSource, index);
body += Drop();
body += LoadLocal(index);
body += LoadLocal(num_named);
body += SmiRelationalOp(Token::kLT);
TargetEntryInstr* has_more_fields;
TargetEntryInstr* no_more_fields;
body += BranchIfTrue(&has_more_fields, &no_more_fields);
Fragment(has_more_fields) + Goto(loop);
Fragment(no_more_fields) + Goto(nsm);
body.current = found;
body += LoadLocal(parsed_function_->receiver_var());
body += LoadLocal(parsed_function_->receiver_var());
body += LoadNativeField(Slot::Record_num_fields());
body += Box(Slot::Record_num_fields().representation());
body += LoadLocal(num_named);
body += SmiBinaryOp(Token::kSUB);
body += LoadLocal(index);
body += SmiBinaryOp(Token::kADD);
body += LoadIndexed(kRecordCid,
/*index_scale*/ compiler::target::kCompressedWordSize);
body += DropTempsPreserveTop(2);
body += Return(TokenPosition::kNoSource);
Fragment throw_nsm(nsm);
throw_nsm += LoadLocal(parsed_function_->receiver_var());
throw_nsm +=

View file

@ -27787,10 +27787,8 @@ intptr_t Record::GetFieldIndexByName(const String& field_name) const {
ASSERT(field_name.IsSymbol());
const intptr_t field_index =
Record::GetPositionalFieldIndexFromFieldName(field_name);
if (field_index >= 0) {
if (field_index < NumPositionalFields()) {
return field_index;
}
if ((field_index >= 0) && (field_index < NumPositionalFields())) {
return field_index;
} else {
const Array& field_names = Array::Handle(this->field_names());
for (intptr_t i = 0, n = field_names.Length(); i < n; ++i) {

View file

@ -30,6 +30,7 @@ dynamic r1 = runtimeTrue ? (2, 3) as dynamic : A();
dynamic r2 = runtimeTrue ? (foo: 'hey') as dynamic : A();
dynamic r3 = runtimeTrue ? const (10, 'a', foo: [1], bar: (50, baz: 60)) as dynamic: A();
dynamic r4 = runtimeTrue ? (foo1, foo2: foo2) as dynamic: A();
dynamic r5 = runtimeTrue ? (10, $1: 20, $999999999999999999: 'meow') as dynamic: A();
main() {
Expect.equals(2, r1.$0);
@ -70,4 +71,10 @@ main() {
Expect.equals('Hi from foo2', r4.foo2(42));
Expect.throwsNoSuchMethodError(() { r4.foo2(42, 42); });
Expect.throwsTypeError(() { r4.foo2('not int'); } );
Expect.equals(10, r5.$0);
Expect.equals(20, r5.$1);
Expect.equals('meow', r5.$999999999999999999);
Expect.throwsNoSuchMethodError(() => r5.$2);
Expect.throwsNoSuchMethodError(() => r5.$999999999999999998);
}