mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:37:43 +00:00
[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:
parent
e3f77e928f
commit
d7852e4786
|
@ -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 +=
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue