Skip to content

Commit

Permalink
[vm] Fix late local variables in AST mode
Browse files Browse the repository at this point in the history
 - Set a flag on LocalVariable if it's late, and store its initializer offset
 - Visit late var init in when visiting var get in scope builder
 - Initialize late vars to the sentinel
 - Check the sentinel and run the initializer in late var get

Bug: #38841
Change-Id: I5500ed133537ab663e2b3d8cfb5cca04233be25e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/126982
Commit-Queue: Liam Appelbe <liama@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
  • Loading branch information
liamappelbe authored and commit-bot@chromium.org committed Dec 6, 2019
1 parent 6267a81 commit de53a2c
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 32 deletions.
85 changes: 69 additions & 16 deletions runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2126,18 +2126,67 @@ Fragment StreamingFlowGraphBuilder::BuildInvalidExpression(
}

Fragment StreamingFlowGraphBuilder::BuildVariableGet(TokenPosition* position) {
(position != NULL) ? * position = ReadPosition() : ReadPosition();
const TokenPosition pos = ReadPosition();
if (position != nullptr) *position = pos;
intptr_t variable_kernel_position = ReadUInt(); // read kernel position.
ReadUInt(); // read relative variable index.
SkipOptionalDartType(); // read promoted type.
return LoadLocal(LookupVariable(variable_kernel_position));
return BuildVariableGetImpl(variable_kernel_position, pos);
}

Fragment StreamingFlowGraphBuilder::BuildVariableGet(uint8_t payload,
TokenPosition* position) {
(position != NULL) ? * position = ReadPosition() : ReadPosition();
const TokenPosition pos = ReadPosition();
if (position != nullptr) *position = pos;
intptr_t variable_kernel_position = ReadUInt(); // read kernel position.
return LoadLocal(LookupVariable(variable_kernel_position));
return BuildVariableGetImpl(variable_kernel_position, pos);
}

Fragment StreamingFlowGraphBuilder::BuildVariableGetImpl(
intptr_t variable_kernel_position,
TokenPosition position) {
LocalVariable* variable = LookupVariable(variable_kernel_position);
if (!variable->is_late()) {
return LoadLocal(variable);
}

// Late variable, so check whether it has been initialized already.
Fragment instructions = LoadLocal(variable);
TargetEntryInstr *is_uninitialized, *is_initialized;
instructions += Constant(Object::sentinel());
instructions += flow_graph_builder_->BranchIfStrictEqual(&is_uninitialized,
&is_initialized);
JoinEntryInstr* join = BuildJoinEntry();

{
AlternativeReadingScope alt(&reader_, variable->late_init_offset());
const bool has_initializer = (ReadTag() != kNothing);

if (has_initializer) {
// If the variable isn't initialized, call the initializer and set it.
Fragment initialize(is_uninitialized);
initialize += BuildExpression();
initialize += StoreLocal(position, variable);
initialize += Drop();
initialize += Goto(join);
} else {
// The variable has no initializer, so throw a LateInitializationError.
Fragment initialize(is_uninitialized);
initialize += flow_graph_builder_->ThrowLateInitializationError(
position, variable->name());
initialize += Goto(join);
}
}

{
// Already initialized, so there's nothing to do.
Fragment already_initialized(is_initialized);
already_initialized += Goto(join);
}

Fragment done = Fragment(instructions.entry, join);
done += LoadLocal(variable);
return done;
}

Fragment StreamingFlowGraphBuilder::BuildVariableSet(TokenPosition* p) {
Expand Down Expand Up @@ -4932,22 +4981,26 @@ Fragment StreamingFlowGraphBuilder::BuildVariableDeclaration() {
VariableDeclarationHelper helper(this);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
T.BuildType(); // read type.
Tag tag = ReadTag(); // read (first part of) initializer.
bool has_initializer = (ReadTag() != kNothing);

Fragment instructions;
if (tag == kNothing) {
if (variable->is_late()) {
// TODO(liama): Treat the field as non-late if the initializer is trivial.
if (has_initializer) {
SkipExpression();
}
instructions += Constant(Object::sentinel());
} else if (!has_initializer) {
instructions += NullConstant();
} else if (helper.IsConst()) {
// Read const initializer form current position.
const Instance& constant_value =
Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression());
variable->SetConstValue(constant_value);
instructions += Constant(constant_value);
} else {
if (helper.IsConst()) {
// Read const initializer form current position.
const Instance& constant_value =
Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression());
variable->SetConstValue(constant_value);
instructions += Constant(constant_value);
} else {
// Initializer
instructions += BuildExpression(); // read (actual) initializer.
}
// Initializer
instructions += BuildExpression(); // read (actual) initializer.
}

// Use position of equal sign if it exists. If the equal sign does not exist
Expand Down
4 changes: 4 additions & 0 deletions runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
Fragment StringInterpolate(TokenPosition position);
Fragment StringInterpolateSingle(TokenPosition position);
Fragment ThrowTypeError();
Fragment ThrowLateInitializationError(TokenPosition position,
const String& name);
Fragment LoadInstantiatorTypeArguments();
Fragment LoadFunctionTypeArguments();
Fragment InstantiateType(const AbstractType& type);
Expand Down Expand Up @@ -276,6 +278,8 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
Fragment BuildInvalidExpression(TokenPosition* position);
Fragment BuildVariableGet(TokenPosition* position);
Fragment BuildVariableGet(uint8_t payload, TokenPosition* position);
Fragment BuildVariableGetImpl(intptr_t variable_kernel_position,
TokenPosition position);
Fragment BuildVariableSet(TokenPosition* position);
Fragment BuildVariableSet(uint8_t payload, TokenPosition* position);
Fragment BuildPropertyGet(TokenPosition* position);
Expand Down
12 changes: 8 additions & 4 deletions runtime/vm/compiler/frontend/kernel_translation_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ class VariableDeclarationHelper {
kConst = 1 << 1,
kCovariant = 1 << 3,
kIsGenericCovariantImpl = 1 << 5,
kLate = 1 << 6,
kRequired = 1 << 7,
};

explicit VariableDeclarationHelper(KernelReaderHelper* helper)
Expand All @@ -404,11 +406,13 @@ class VariableDeclarationHelper {
void SetNext(Field field) { next_read_ = field; }
void SetJustRead(Field field) { next_read_ = field + 1; }

bool IsConst() { return (flags_ & kConst) != 0; }
bool IsFinal() { return (flags_ & kFinal) != 0; }
bool IsCovariant() { return (flags_ & kCovariant) != 0; }
bool IsConst() const { return (flags_ & kConst) != 0; }
bool IsFinal() const { return (flags_ & kFinal) != 0; }
bool IsCovariant() const { return (flags_ & kCovariant) != 0; }
bool IsLate() const { return (flags_ & kLate) != 0; }
bool IsRequired() const { return (flags_ & kRequired) != 0; }

bool IsGenericCovariantImpl() {
bool IsGenericCovariantImpl() const {
return (flags_ & kIsGenericCovariantImpl) != 0;
}

Expand Down
25 changes: 22 additions & 3 deletions runtime/vm/compiler/frontend/scope_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -653,14 +653,14 @@ void ScopeBuilder::VisitExpression() {
helper_.ReadUInt(); // read kernel position.
helper_.ReadUInt(); // read relative variable index.
helper_.SkipOptionalDartType(); // read promoted type.
LookupVariable(variable_kernel_offset);
VisitVariableGet(variable_kernel_offset);
return;
}
case kSpecializedVariableGet: {
helper_.ReadPosition(); // read position.
intptr_t variable_kernel_offset =
helper_.ReadUInt(); // read kernel position.
LookupVariable(variable_kernel_offset);
VisitVariableGet(variable_kernel_offset);
return;
}
case kVariableSet: {
Expand Down Expand Up @@ -1260,6 +1260,7 @@ void ScopeBuilder::VisitVariableDeclaration() {
? GenerateName(":var", name_index_++)
: H.DartSymbolObfuscate(helper.name_index_);

intptr_t initializer_offset = helper_.ReaderOffset();
Tag tag = helper_.ReadTag(); // read (first part of) initializer.
if (tag == kSomething) {
VisitExpression(); // read (actual) initializer.
Expand All @@ -1276,6 +1277,10 @@ void ScopeBuilder::VisitVariableDeclaration() {
if (helper.IsFinal()) {
variable->set_is_final();
}
if (helper.IsLate()) {
variable->set_is_late();
variable->set_late_init_offset(initializer_offset);
}
// Lift the two special async vars out of the function body scope, into the
// outer function declaration scope.
// This way we can allocate them in the outermost context at fixed indices,
Expand Down Expand Up @@ -1710,7 +1715,20 @@ void ScopeBuilder::AddSwitchVariable() {
}
}

void ScopeBuilder::LookupVariable(intptr_t declaration_binary_offset) {
void ScopeBuilder::VisitVariableGet(intptr_t declaration_binary_offset) {
LocalVariable* variable = LookupVariable(declaration_binary_offset);
if (variable->is_late()) {
// Late variable initializer expressions may also contain local variables
// that need to be captured.
AlternativeReadingScope alt(&helper_.reader_, variable->late_init_offset());
if (helper_.ReadTag() != kNothing) {
VisitExpression();
}
}
}

LocalVariable* ScopeBuilder::LookupVariable(
intptr_t declaration_binary_offset) {
LocalVariable* variable = result_->locals.Lookup(declaration_binary_offset);
if (variable == NULL) {
// We have not seen a declaration of the variable, so it must be the
Expand Down Expand Up @@ -1745,6 +1763,7 @@ void ScopeBuilder::LookupVariable(intptr_t declaration_binary_offset) {
} else {
ASSERT(variable->owner()->function_level() == scope_->function_level());
}
return variable;
}

StringIndex ScopeBuilder::GetNameFromVariableDeclaration(
Expand Down
3 changes: 2 additions & 1 deletion runtime/vm/compiler/frontend/scope_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ScopeBuilder {
void VisitStatement();
void VisitArguments();
void VisitVariableDeclaration();
void VisitVariableGet(intptr_t declaration_binary_offset);
void VisitDartType();
void VisitInterfaceType(bool simple);
void VisitFunctionType(bool simple);
Expand Down Expand Up @@ -110,7 +111,7 @@ class ScopeBuilder {
// Record an assignment or reference to a variable. If the occurrence is
// in a nested function, ensure that the variable is handled properly as a
// captured variable.
void LookupVariable(intptr_t declaration_binary_offset);
LocalVariable* LookupVariable(intptr_t declaration_binary_offset);

StringIndex GetNameFromVariableDeclaration(intptr_t kernel_offset,
const Function& function);
Expand Down
44 changes: 38 additions & 6 deletions runtime/vm/object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15700,22 +15700,54 @@ void ContextScope::SetNameAt(intptr_t scope_index, const String& name) const {
StorePointer(&(VariableDescAddr(scope_index)->name), name.raw());
}

void ContextScope::ClearFlagsAt(intptr_t scope_index) const {
StoreSmi(&(VariableDescAddr(scope_index)->flags), 0);
}

bool ContextScope::GetFlagAt(intptr_t scope_index, intptr_t mask) const {
return (Smi::Value(VariableDescAddr(scope_index)->flags) & mask) != 0;
}

void ContextScope::SetFlagAt(intptr_t scope_index,
intptr_t mask,
bool value) const {
intptr_t flags = Smi::Value(VariableDescAddr(scope_index)->flags);
StoreSmi(&(VariableDescAddr(scope_index)->flags),
Smi::New(value ? flags | mask : flags & ~mask));
}

bool ContextScope::IsFinalAt(intptr_t scope_index) const {
return Bool::Handle(VariableDescAddr(scope_index)->is_final).value();
return GetFlagAt(scope_index, RawContextScope::VariableDesc::kIsFinal);
}

void ContextScope::SetIsFinalAt(intptr_t scope_index, bool is_final) const {
StorePointer(&(VariableDescAddr(scope_index)->is_final),
Bool::Get(is_final).raw());
SetFlagAt(scope_index, RawContextScope::VariableDesc::kIsFinal, is_final);
}

bool ContextScope::IsLateAt(intptr_t scope_index) const {
return GetFlagAt(scope_index, RawContextScope::VariableDesc::kIsLate);
}

void ContextScope::SetIsLateAt(intptr_t scope_index, bool is_late) const {
SetFlagAt(scope_index, RawContextScope::VariableDesc::kIsLate, is_late);
}

bool ContextScope::IsConstAt(intptr_t scope_index) const {
return Bool::Handle(VariableDescAddr(scope_index)->is_const).value();
return GetFlagAt(scope_index, RawContextScope::VariableDesc::kIsConst);
}

void ContextScope::SetIsConstAt(intptr_t scope_index, bool is_const) const {
StorePointer(&(VariableDescAddr(scope_index)->is_const),
Bool::Get(is_const).raw());
SetFlagAt(scope_index, RawContextScope::VariableDesc::kIsConst, is_const);
}

intptr_t ContextScope::LateInitOffsetAt(intptr_t scope_index) const {
return Smi::Value(VariableDescAddr(scope_index)->late_init_offset);
}

void ContextScope::SetLateInitOffsetAt(intptr_t scope_index,
intptr_t late_init_offset) const {
StoreSmi(&(VariableDescAddr(scope_index)->late_init_offset),
Smi::New(late_init_offset));
}

RawAbstractType* ContextScope::TypeAt(intptr_t scope_index) const {
Expand Down
12 changes: 12 additions & 0 deletions runtime/vm/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -6102,9 +6102,18 @@ class ContextScope : public Object {
RawString* NameAt(intptr_t scope_index) const;
void SetNameAt(intptr_t scope_index, const String& name) const;

void ClearFlagsAt(intptr_t scope_index) const;

bool IsFinalAt(intptr_t scope_index) const;
void SetIsFinalAt(intptr_t scope_index, bool is_final) const;

bool IsLateAt(intptr_t scope_index) const;
void SetIsLateAt(intptr_t scope_index, bool is_late) const;

intptr_t LateInitOffsetAt(intptr_t scope_index) const;
void SetLateInitOffsetAt(intptr_t scope_index,
intptr_t late_init_offset) const;

bool IsConstAt(intptr_t scope_index) const;
void SetIsConstAt(intptr_t scope_index, bool is_const) const;

Expand Down Expand Up @@ -6152,6 +6161,9 @@ class ContextScope : public Object {
return raw_ptr()->VariableDescAddr(index);
}

bool GetFlagAt(intptr_t scope_index, intptr_t mask) const;
void SetFlagAt(intptr_t scope_index, intptr_t mask, bool value) const;

FINAL_HEAP_OBJECT_IMPLEMENTATION(ContextScope, Object);
friend class Class;
friend class Object;
Expand Down
7 changes: 5 additions & 2 deletions runtime/vm/raw_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -1805,8 +1805,11 @@ class RawContextScope : public RawObject {
RawSmi* declaration_token_pos;
RawSmi* token_pos;
RawString* name;
RawBool* is_final;
RawBool* is_const;
RawSmi* flags;
static constexpr intptr_t kIsFinal = 0x1;
static constexpr intptr_t kIsConst = 0x2;
static constexpr intptr_t kIsLate = 0x4;
RawSmi* late_init_offset;
union {
RawAbstractType* type;
RawInstance* value; // iff is_const is true
Expand Down
11 changes: 11 additions & 0 deletions runtime/vm/scopes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,13 @@ RawContextScope* LocalScope::PreserveOuterScope(
context_scope.SetDeclarationTokenIndexAt(
captured_idx, variable->declaration_token_pos());
context_scope.SetNameAt(captured_idx, variable->name());
context_scope.ClearFlagsAt(captured_idx);
context_scope.SetIsFinalAt(captured_idx, variable->is_final());
context_scope.SetIsLateAt(captured_idx, variable->is_late());
if (variable->is_late()) {
context_scope.SetLateInitOffsetAt(captured_idx,
variable->late_init_offset());
}
context_scope.SetIsConstAt(captured_idx, variable->IsConst());
if (variable->IsConst()) {
context_scope.SetConstValueAt(captured_idx, *variable->ConstValue());
Expand Down Expand Up @@ -645,6 +651,10 @@ LocalScope* LocalScope::RestoreOuterScope(const ContextScope& context_scope) {
if (context_scope.IsFinalAt(i)) {
variable->set_is_final();
}
if (context_scope.IsLateAt(i)) {
variable->set_is_late();
variable->set_late_init_offset(context_scope.LateInitOffsetAt(i));
}
// Create a fake owner scope describing the index and context level of the
// variable. Function level and loop level are unused (set to 0), since
// context level has already been assigned.
Expand Down Expand Up @@ -691,6 +701,7 @@ RawContextScope* LocalScope::CreateImplicitClosureScope(const Function& func) {
context_scope.SetTokenIndexAt(0, func.token_pos());
context_scope.SetDeclarationTokenIndexAt(0, func.token_pos());
context_scope.SetNameAt(0, Symbols::This());
context_scope.ClearFlagsAt(0);
context_scope.SetIsFinalAt(0, true);
context_scope.SetIsConstAt(0, false);
const AbstractType& type = AbstractType::Handle(func.ParameterTypeAt(0));
Expand Down
Loading

0 comments on commit de53a2c

Please sign in to comment.