Add local variable declaration token position to service protocol

- [x] Add three public fields to the "BoundVariable" service type: declarationTokenPos, visibleStartTokenPos, and visibleEndTokenPos. (naming suggestions welcome!)
- [x] Extend LocalVarDescriptors to hold the declaration token position (it already had the scope visibility boundaries).
- [x] Extend ContextScope to hold the declaration token position.
- [x] Add a unit test which verifies this works for local variables, function parameters, and closure captured variables.

Fixes https://github.com/dart-lang/sdk/issues/25569

BUG=
R=rmacnak@google.com

Review URL: https://codereview.chromium.org/2419013004 .
This commit is contained in:
John McCutchan 2016-10-17 11:18:24 -07:00
parent 724ac40b8d
commit 8061bd5a8a
25 changed files with 328 additions and 57 deletions

View file

@ -3254,6 +3254,7 @@ class Script extends HeapObject implements M.Script {
Script._empty(ServiceObjectOwner owner) : super._empty(owner);
/// Retrieves line number [line] if it exists.
ScriptLine getLine(int line) {
assert(_loaded);
assert(line >= 1);
@ -3261,10 +3262,12 @@ class Script extends HeapObject implements M.Script {
}
/// This function maps a token position to a line number.
/// The VM considers the first line to be line 1.
int tokenToLine(int tokenPos) => _tokenToLine[tokenPos];
Map _tokenToLine = {};
/// This function maps a token position to a column number.
/// The VM considers the first column to be column 1.
int tokenToCol(int tokenPos) => _tokenToCol[tokenPos];
Map _tokenToCol = {};
@ -3327,7 +3330,7 @@ class Script extends HeapObject implements M.Script {
static bool _isIdentifierChar(int c) {
if (_isInitialIdentifierChar(c)) return true;
return c >= 48 && c <= 75; // Digit
return c >= 48 && c <= 57; // Digit
}
void _update(Map map, bool mapIsRef) {
@ -3408,6 +3411,28 @@ class Script extends HeapObject implements M.Script {
}
}
// Note, this may return source beyond the token length if [guessTokenLength]
// fails.
String getToken(int tokenPos) {
final int line = tokenToLine(tokenPos);
int column = tokenToCol(tokenPos);
if ((line == null) || (column == null)) {
return null;
}
// Line and column numbers start at 1 in the VM.
column -= 1;
String sourceLine = getLine(line).text;
if (sourceLine == null) {
return null;
}
final int length = guessTokenLength(line, column);
if (length == null) {
return sourceLine.substring(column);
} else {
return sourceLine.substring(column, column + length);
}
}
void _addBreakpoint(Breakpoint bpt) {
var line;
if (bpt.location.tokenPos != null) {
@ -3610,13 +3635,20 @@ class LocalVarDescriptor implements M.LocalVarDescriptorsRef {
final String id;
final String name;
final int index;
final int declarationPos;
final int beginPos;
final int endPos;
final int scopeId;
final String kind;
LocalVarDescriptor(this.id, this.name, this.index, this.beginPos, this.endPos,
this.scopeId, this.kind);
LocalVarDescriptor(this.id,
this.name,
this.index,
this.declarationPos,
this.beginPos,
this.endPos,
this.scopeId,
this.kind);
}
class LocalVarDescriptors extends ServiceObject {
@ -3638,12 +3670,13 @@ class LocalVarDescriptors extends ServiceObject {
var id = descriptor['name'];
var name = descriptor['name'];
var index = descriptor['index'];
var beginPos = descriptor['beginPos'];
var endPos = descriptor['endPos'];
var declarationPos = descriptor['declarationTokenPos'];
var beginPos = descriptor['scopeStartTokenPos'];
var endPos = descriptor['scopeEndTokenPos'];
var scopeId = descriptor['scopeId'];
var kind = descriptor['kind'].trim();
descriptors.add(new LocalVarDescriptor(
id, name, index, beginPos, endPos, scopeId, kind));
id, name, index, declarationPos, beginPos, endPos, scopeId, kind));
}
}
}

View file

@ -0,0 +1,101 @@
// Copyright (c) 2016, 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.
// VMOptions=--error_on_bad_type --error_on_bad_override --verbose_debug
import 'package:observatory/service_io.dart';
import 'package:unittest/unittest.dart';
import 'service_test_common.dart';
import 'test_helper.dart';
import 'dart:developer';
testParameters(int jjjj, int oooo, [int hhhh, int nnnn]) {
debugger();
}
testMain() {
int xxx, yyyy, zzzzz;
for (int i = 0; i < 1; i++) {
var foo = () {
};
debugger();
}
var bar = () {
print(xxx);
print(yyyy);
debugger();
};
bar();
testParameters(0, 0);
}
var tests = [
hasStoppedAtBreakpoint,
stoppedInFunction('testMain'),
(Isolate isolate) async {
var stack = await isolate.getStack();
expect(stack.type, equals('Stack'));
expect(stack['frames'].length, greaterThanOrEqualTo(1));
// Grab the top frame.
Frame frame = stack['frames'][0];
// Grab the script.
Script script = frame.location.script;
await script.load();
// Ensure that the token at each declaration position is the name of the
// variable.
for (var variable in frame.variables) {
final int declarationTokenPos = variable['declarationTokenPos'];
final String name = variable['name'];
final String token = script.getToken(declarationTokenPos);
expect(name, token);
}
},
resumeIsolate,
hasStoppedAtBreakpoint,
// We have stopped in the anonymous closure assigned to bar. Verify that
// variables captured in the context have valid declaration positions.
(Isolate isolate) async {
var stack = await isolate.getStack();
expect(stack.type, equals('Stack'));
expect(stack['frames'].length, greaterThanOrEqualTo(1));
// Grab the top frame.
Frame frame = stack['frames'][0];
// Grab the script.
Script script = frame.location.script;
await script.load();
print(frame);
expect(frame.variables.length, greaterThanOrEqualTo(1));
for (var variable in frame.variables) {
final int declarationTokenPos = variable['declarationTokenPos'];
final String name = variable['name'];
final String token = script.getToken(declarationTokenPos);
expect(name, token);
}
},
resumeIsolate,
hasStoppedAtBreakpoint,
stoppedInFunction('testParameters'),
(Isolate isolate) async {
var stack = await isolate.getStack();
expect(stack.type, equals('Stack'));
expect(stack['frames'].length, greaterThanOrEqualTo(1));
// Grab the top frame.
Frame frame = stack['frames'][0];
// Grab the script.
Script script = frame.location.script;
await script.load();
// Ensure that the token at each declaration position is the name of the
// variable.
expect(frame.variables.length, greaterThanOrEqualTo(1));
for (var variable in frame.variables) {
final int declarationTokenPos = variable['declarationTokenPos'];
final String name = variable['name'];
final String token = script.getToken(declarationTokenPos);
expect(name, token);
}
}
];
main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);

View file

@ -111,7 +111,8 @@ LocalVariable* LetNode::AddInitializer(AstNode* node) {
OS::SNPrint(name, sizeof(name), ":lt%s_%" Pd "",
token_pos().ToCString(), vars_.length());
LocalVariable* temp_var =
new LocalVariable(token_pos(),
new LocalVariable(TokenPosition::kNoSource,
token_pos(),
String::ZoneHandle(zone, Symbols::New(thread, name)),
Object::dynamic_type());
vars_.Add(temp_var);

View file

@ -18,6 +18,7 @@ TEST_CASE(AstPrinter) {
const TokenPosition kPos = TokenPosition::kNoSource;
LocalVariable* v =
new LocalVariable(kPos,
kPos,
String::ZoneHandle(Symbols::New(thread, "wurscht")),
Type::ZoneHandle(Type::DynamicType()));
v->set_index(5);

View file

@ -14,6 +14,7 @@ namespace dart {
TEST_CASE(Ast) {
LocalVariable* v = new LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
String::ZoneHandle(Symbols::New(thread, "v")),
Type::ZoneHandle(Type::DynamicType()));
@ -26,6 +27,7 @@ TEST_CASE(Ast) {
EXPECT_EQ(1, v->index());
LocalVariable* p = new LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
String::ZoneHandle(Symbols::New(thread, "p")),
Type::ZoneHandle(Type::DynamicType()));

View file

@ -77,7 +77,10 @@ LocalVariable* AwaitTransformer::EnsureCurrentTempVar() {
if (await_tmp == NULL) {
// We need a new temp variable; add it to the function's top scope.
await_tmp = new(Z) LocalVariable(
TokenPosition::kNoSource, symbol, Object::dynamic_type());
TokenPosition::kNoSource,
TokenPosition::kNoSource,
symbol,
Object::dynamic_type());
async_temp_scope_->AddVariable(await_tmp);
// After adding it to the top scope, we can look it up from the preamble.
// The following call includes an ASSERT check.

View file

@ -1728,7 +1728,7 @@ DEFINE_RUNTIME_ENTRY(StackOverflow, 0) {
FLAG_precompiled_runtime ? 0 : frame->NumLocalVariables();
TokenPosition unused = TokenPosition::kNoSource;
for (intptr_t v = 0; v < num_vars; v++) {
frame->VariableAt(v, &var_name, &unused, &unused, &var_value);
frame->VariableAt(v, &var_name, &unused, &unused, &unused, &var_value);
}
}
FLAG_stacktrace_every = saved_stacktrace_every;

View file

@ -50,7 +50,7 @@ static LocalVariable* NewTestLocalVariable(const char* name) {
const String& variable_name = String::ZoneHandle(
Symbols::New(Thread::Current(), name));
const Type& variable_type = Type::ZoneHandle(Type::DynamicType());
return new LocalVariable(kPos, variable_name, variable_type);
return new LocalVariable(kPos, kPos, variable_name, variable_type);
}

View file

@ -896,8 +896,9 @@ void ActivationFrame::PrintContextMismatchError(
void ActivationFrame::VariableAt(intptr_t i,
String* name,
TokenPosition* token_pos,
TokenPosition* end_pos,
TokenPosition* declaration_token_pos,
TokenPosition* visible_start_token_pos,
TokenPosition* visible_end_token_pos,
Object* value) {
GetDescIndices();
ASSERT(i < desc_indices_.length());
@ -908,10 +909,12 @@ void ActivationFrame::VariableAt(intptr_t i,
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(desc_index, &var_info);
ASSERT(token_pos != NULL);
*token_pos = var_info.begin_pos;
ASSERT(end_pos != NULL);
*end_pos = var_info.end_pos;
ASSERT(declaration_token_pos != NULL);
*declaration_token_pos = var_info.declaration_pos;
ASSERT(visible_start_token_pos != NULL);
*visible_start_token_pos = var_info.begin_pos;
ASSERT(visible_end_token_pos != NULL);
*visible_end_token_pos = var_info.end_pos;
ASSERT(value != NULL);
const int8_t kind = var_info.kind();
if (kind == RawLocalVarDescriptors::kStackVar) {
@ -966,7 +969,7 @@ RawArray* ActivationFrame::GetLocalVariables() {
const Array& list = Array::Handle(Array::New(2 * num_variables));
for (intptr_t i = 0; i < num_variables; i++) {
TokenPosition ignore;
VariableAt(i, &var_name, &ignore, &ignore, &value);
VariableAt(i, &var_name, &ignore, &ignore, &ignore, &value);
list.SetAt(2 * i, var_name);
list.SetAt((2 * i) + 1, value);
}
@ -981,7 +984,7 @@ RawObject* ActivationFrame::GetReceiver() {
Instance& value = Instance::Handle();
for (intptr_t i = 0; i < num_variables; i++) {
TokenPosition ignore;
VariableAt(i, &var_name, &ignore, &ignore, &value);
VariableAt(i, &var_name, &ignore, &ignore, &ignore, &value);
if (var_name.Equals(Symbols::This())) {
return value.raw();
}
@ -1011,7 +1014,7 @@ RawObject* ActivationFrame::Evaluate(const String& expr) {
intptr_t num_variables = desc_indices_.length();
for (intptr_t i = 0; i < num_variables; i++) {
TokenPosition ignore;
VariableAt(i, &name, &ignore, &ignore, &value);
VariableAt(i, &name, &ignore, &ignore, &ignore, &value);
if (!name.Equals(Symbols::This()) && !IsSyntheticVariableName(name)) {
if (IsPrivateVariableName(name)) {
name = String::ScrubName(name);
@ -1087,20 +1090,27 @@ void ActivationFrame::PrintToJSONObject(JSONObject* jsobj,
for (intptr_t v = 0; v < num_vars; v++) {
String& var_name = String::Handle();
Instance& var_value = Instance::Handle();
TokenPosition token_pos;
TokenPosition end_token_pos;
VariableAt(v, &var_name, &token_pos, &end_token_pos, &var_value);
TokenPosition declaration_token_pos;
TokenPosition visible_start_token_pos;
TokenPosition visible_end_token_pos;
VariableAt(v,
&var_name,
&declaration_token_pos,
&visible_start_token_pos,
&visible_end_token_pos,
&var_value);
if (var_name.raw() != Symbols::AsyncOperation().raw()) {
JSONObject jsvar(&jsvars);
jsvar.AddProperty("type", "BoundVariable");
var_name = String::ScrubName(var_name);
jsvar.AddProperty("name", var_name.ToCString());
jsvar.AddProperty("value", var_value, !full);
// TODO(turnidge): Do we really want to provide this on every
// stack dump? Should be associated with the function object, I
// think, and not the stack frame.
jsvar.AddProperty("_tokenPos", token_pos);
jsvar.AddProperty("_endTokenPos", end_token_pos);
// Where was the variable declared?
jsvar.AddProperty("declarationTokenPos", declaration_token_pos);
// When the variable becomes visible to the scope.
jsvar.AddProperty("scopeStartTokenPos", visible_start_token_pos);
// When the variable stops being visible to the scope.
jsvar.AddProperty("scopeEndTokenPos", visible_end_token_pos);
}
}
}

View file

@ -285,8 +285,9 @@ class ActivationFrame : public ZoneAllocated {
void VariableAt(intptr_t i,
String* name,
TokenPosition* token_pos,
TokenPosition* end_pos,
TokenPosition* declaration_token_pos,
TokenPosition* visible_start_token_pos,
TokenPosition* visible_end_token_pos,
Object* value);
RawArray* GetLocalVariables();

View file

@ -2294,6 +2294,7 @@ LocalVariable* EffectGraphVisitor::EnterTempLocalScope(Value* value) {
OS::SNPrint(name, 64, ":tmp_local%" Pd, index);
LocalVariable* var =
new(Z) LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource,
String::ZoneHandle(Z, Symbols::New(T, name)),
*value->Type()->ToAbstractType());
var->set_index(index);
@ -4026,6 +4027,7 @@ void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) {
// Create a temporary local describing the original position.
const String& temp_name = Symbols::TempParam();
LocalVariable* temp_local = new(Z) LocalVariable(
TokenPosition::kNoSource, // Token index.
TokenPosition::kNoSource, // Token index.
temp_name,
Object::dynamic_type()); // Type.

View file

@ -41,13 +41,19 @@ void ScopeBuilder::ExitScope() { scope_ = scope_->parent(); }
LocalVariable* ScopeBuilder::MakeVariable(const dart::String& name) {
return new (Z)
LocalVariable(TokenPosition::kNoSource, name, Object::dynamic_type());
LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource,
name,
Object::dynamic_type());
}
LocalVariable* ScopeBuilder::MakeVariable(const dart::String& name,
const Type& type) {
return new (Z) LocalVariable(TokenPosition::kNoSource, name, type);
return new (Z) LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource,
name,
type);
}
@ -2503,7 +2509,10 @@ LocalVariable* FlowGraphBuilder::MakeTemporary() {
intptr_t index = stack_->definition()->temp_index();
OS::SNPrint(name, 64, ":temp%" Pd, index);
LocalVariable* variable = new (Z) LocalVariable(
TokenPosition::kNoSource, H.DartSymbol(name), Object::dynamic_type());
TokenPosition::kNoSource,
TokenPosition::kNoSource,
H.DartSymbol(name),
Object::dynamic_type());
// Set the index relative to the base of the expression stack including
// outgoing arguments.
variable->set_index(parsed_function_->first_stack_local_index() -
@ -2764,7 +2773,9 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFunction(FunctionNode* function,
// create one directly.
LocalVariable* parameter =
new (Z) LocalVariable(TokenPosition::kNoSource,
Symbols::TempParam(), Object::dynamic_type());
TokenPosition::kNoSource,
Symbols::TempParam(),
Object::dynamic_type());
parameter->set_index(parameter_index);
// Mark the stack variable so it will be ignored by the code for
// try/catch.

View file

@ -14811,6 +14811,21 @@ void ContextScope::SetTokenIndexAt(intptr_t scope_index,
}
TokenPosition ContextScope::DeclarationTokenIndexAt(
intptr_t scope_index) const {
return TokenPosition(
Smi::Value(VariableDescAddr(scope_index)->declaration_token_pos));
}
void ContextScope::SetDeclarationTokenIndexAt(
intptr_t scope_index,
TokenPosition declaration_token_pos) const {
StoreSmi(&VariableDescAddr(scope_index)->declaration_token_pos,
Smi::New(declaration_token_pos.value()));
}
RawString* ContextScope::NameAt(intptr_t scope_index) const {
return VariableDescAddr(scope_index)->name;
}

View file

@ -5151,6 +5151,10 @@ class ContextScope : public Object {
TokenPosition TokenIndexAt(intptr_t scope_index) const;
void SetTokenIndexAt(intptr_t scope_index, TokenPosition token_pos) const;
TokenPosition DeclarationTokenIndexAt(intptr_t scope_index) const;
void SetDeclarationTokenIndexAt(intptr_t scope_index,
TokenPosition declaration_token_pos) const;
RawString* NameAt(intptr_t scope_index) const;
void SetNameAt(intptr_t scope_index, const String& name) const;

View file

@ -736,8 +736,9 @@ void LocalVarDescriptors::PrintJSONImpl(JSONStream* stream,
JSONObject var(&members);
var.AddProperty("name", var_name.ToCString());
var.AddProperty("index", static_cast<intptr_t>(info.index()));
var.AddProperty("beginPos", info.begin_pos);
var.AddProperty("endPos", info.end_pos);
var.AddProperty("declarationTokenPos", info.declaration_pos);
var.AddProperty("scopeStartTokenPos", info.begin_pos);
var.AddProperty("scopeEndTokenPos", info.end_pos);
var.AddProperty("scopeId", static_cast<intptr_t>(info.scope_id));
var.AddProperty("kind", KindToCString(info.kind()));
}

View file

@ -2535,17 +2535,26 @@ VM_TEST_CASE(ContextScope) {
const Type& dynamic_type = Type::ZoneHandle(Type::DynamicType());
const String& a = String::ZoneHandle(Symbols::New(thread, "a"));
LocalVariable* var_a =
new LocalVariable(TokenPosition::kNoSource, a, dynamic_type);
new LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource,
a,
dynamic_type);
parent_scope->AddVariable(var_a);
const String& b = String::ZoneHandle(Symbols::New(thread, "b"));
LocalVariable* var_b =
new LocalVariable(TokenPosition::kNoSource, b, dynamic_type);
new LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource,
b,
dynamic_type);
local_scope->AddVariable(var_b);
const String& c = String::ZoneHandle(Symbols::New(thread, "c"));
LocalVariable* var_c =
new LocalVariable(TokenPosition::kNoSource, c, dynamic_type);
new LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource,
c,
dynamic_type);
parent_scope->AddVariable(var_c);
bool test_only = false; // Please, insert alias.

View file

@ -244,6 +244,7 @@ LocalVariable* ParsedFunction::EnsureExpressionTemp() {
if (!has_expression_temp_var()) {
LocalVariable* temp =
new (Z) LocalVariable(function_.token_pos(),
function_.token_pos(),
Symbols::ExprTemp(),
Object::dynamic_type());
ASSERT(temp != NULL);
@ -257,6 +258,7 @@ LocalVariable* ParsedFunction::EnsureExpressionTemp() {
void ParsedFunction::EnsureFinallyReturnTemp(bool is_async) {
if (!has_finally_return_temp_var()) {
LocalVariable* temp = new(Z) LocalVariable(
function_.token_pos(),
function_.token_pos(),
Symbols::FinallyRetVal(),
Object::dynamic_type());
@ -3137,6 +3139,7 @@ SequenceNode* Parser::MakeImplicitConstructor(const Function& func) {
OpenFunctionBlock(func);
LocalVariable* receiver = new LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::This(),
*ReceiverType(current_class()));
@ -3185,6 +3188,7 @@ SequenceNode* Parser::MakeImplicitConstructor(const Function& func) {
forwarding_args = new ArgumentListNode(ST(ctor_pos));
for (int i = 1; i < func.NumParameters(); i++) {
LocalVariable* param = new LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
String::ZoneHandle(Z, func.ParameterNameAt(i)),
Object::dynamic_type());
@ -7101,11 +7105,13 @@ void Parser::AddContinuationVariables() {
// var :await_jump_var;
// var :await_ctx_var;
LocalVariable* await_jump_var = new (Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::AwaitJumpVar(),
Object::dynamic_type());
current_block_->scope->AddVariable(await_jump_var);
LocalVariable* await_ctx_var = new (Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::AwaitContextVar(),
Object::dynamic_type());
@ -7120,21 +7126,25 @@ void Parser::AddAsyncClosureVariables() {
// var :async_catch_error_callback;
// var :async_completer;
LocalVariable* async_op_var = new(Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::AsyncOperation(),
Object::dynamic_type());
current_block_->scope->AddVariable(async_op_var);
LocalVariable* async_then_callback_var = new(Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::AsyncThenCallback(),
Object::dynamic_type());
current_block_->scope->AddVariable(async_then_callback_var);
LocalVariable* async_catch_error_callback_var = new(Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::AsyncCatchErrorCallback(),
Object::dynamic_type());
current_block_->scope->AddVariable(async_catch_error_callback_var);
LocalVariable* async_completer = new(Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::AsyncCompleter(),
Object::dynamic_type());
@ -7154,21 +7164,25 @@ void Parser::AddAsyncGeneratorVariables() {
// These variables are used to store the async generator closure containing
// the body of the async* function. They are used by the await operator.
LocalVariable* controller_var = new(Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::Controller(),
Object::dynamic_type());
current_block_->scope->AddVariable(controller_var);
LocalVariable* async_op_var = new(Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::AsyncOperation(),
Object::dynamic_type());
current_block_->scope->AddVariable(async_op_var);
LocalVariable* async_then_callback_var = new(Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::AsyncThenCallback(),
Object::dynamic_type());
current_block_->scope->AddVariable(async_then_callback_var);
LocalVariable* async_catch_error_callback_var = new(Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
Symbols::AsyncCatchErrorCallback(),
Object::dynamic_type());
@ -7658,7 +7672,10 @@ void Parser::AddFormalParamsToScope(const ParamList* params,
ASSERT(!is_top_level_ || param_desc.type->IsResolved());
const String* name = param_desc.name;
LocalVariable* parameter = new(Z) LocalVariable(
param_desc.name_pos, *name, *param_desc.type);
param_desc.name_pos,
param_desc.name_pos,
*name,
*param_desc.type);
if (!scope->InsertParameterAt(i, parameter)) {
ReportError(param_desc.name_pos,
"name '%s' already exists in scope",
@ -7770,7 +7787,10 @@ AstNode* Parser::ParseVariableDeclaration(const AbstractType& type,
is_const, kConsumeCascades, await_preamble);
const TokenPosition expr_end_pos = TokenPos();
variable = new(Z) LocalVariable(
expr_end_pos, ident, type);
ident_pos,
expr_end_pos,
ident,
type);
initialization = new(Z) StoreLocalNode(
assign_pos, variable, expr);
if (is_const) {
@ -7783,7 +7803,10 @@ AstNode* Parser::ParseVariableDeclaration(const AbstractType& type,
} else {
// Initialize variable with null.
variable = new(Z) LocalVariable(
assign_pos, ident, type);
ident_pos,
assign_pos,
ident,
type);
AstNode* null_expr = new(Z) LiteralNode(ident_pos, Object::null_instance());
initialization = new(Z) StoreLocalNode(
ident_pos, variable, null_expr);
@ -7920,6 +7943,7 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) {
result_type = Type::DynamicType();
const TokenPosition function_pos = TokenPos();
TokenPosition function_name_pos = TokenPosition::kNoSource;
TokenPosition metadata_pos = TokenPosition::kNoSource;
if (is_literal) {
ASSERT(CurrentToken() == Token::kLPAREN || CurrentToken() == Token::kLT);
@ -7934,7 +7958,7 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) {
// referring to a not yet declared function type parameter.
result_type = ParseType(ClassFinalizer::kDoNotResolve);
}
const TokenPosition name_pos = TokenPos();
function_name_pos = TokenPos();
variable_name = ExpectIdentifier("function name expected");
function_name = variable_name;
@ -7947,7 +7971,7 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) {
ASSERT(!script_.IsNull());
intptr_t line_number;
script_.GetTokenLocation(previous_pos, &line_number, NULL);
ReportError(name_pos,
ReportError(function_name_pos,
"identifier '%s' previously used in line %" Pd "",
function_name->ToCString(),
line_number);
@ -8026,7 +8050,8 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) {
// Add the function variable to the scope before parsing the function in
// order to allow self reference from inside the function.
function_variable = new(Z) LocalVariable(function_pos,
function_variable = new(Z) LocalVariable(function_name_pos,
function_pos,
*variable_name,
function_type);
function_variable->set_is_final();
@ -8838,7 +8863,7 @@ AstNode* Parser::ParseSwitchStatement(String* label_name) {
expr_pos));
temp_var_type.SetIsFinalized();
LocalVariable* temp_variable = new(Z) LocalVariable(
expr_pos, Symbols::SwitchExpr(), temp_var_type);
expr_pos, expr_pos, Symbols::SwitchExpr(), temp_var_type);
current_block_->scope->AddVariable(temp_variable);
AstNode* save_switch_expr = new(Z) StoreLocalNode(
expr_pos, temp_variable, switch_expr);
@ -9128,7 +9153,7 @@ AstNode* Parser::ParseAwaitForStatement(String* label_name) {
ctor_args);
const AbstractType& iterator_type = Object::dynamic_type();
LocalVariable* iterator_var = new(Z) LocalVariable(
stream_expr_pos, Symbols::ForInIter(), iterator_type);
stream_expr_pos, stream_expr_pos, Symbols::ForInIter(), iterator_type);
current_block_->scope->AddVariable(iterator_var);
AstNode* iterator_init =
new(Z) StoreLocalNode(stream_expr_pos, iterator_var, ctor_call);
@ -9213,6 +9238,7 @@ AstNode* Parser::ParseAwaitForStatement(String* label_name) {
// loop block, so it gets put in the loop context level.
LocalVariable* loop_var =
new(Z) LocalVariable(loop_var_assignment_pos,
loop_var_assignment_pos,
*loop_var_name,
loop_var_type);;
if (loop_var_is_final) {
@ -9388,6 +9414,7 @@ AstNode* Parser::ParseForInStatement(TokenPosition forin_pos,
loop_var_type = ParseConstFinalVarOrType(
I->type_checks() ? ClassFinalizer::kCanonicalize :
ClassFinalizer::kIgnore);
loop_var_pos = TokenPos();
loop_var_name = ExpectIdentifier("variable name expected");
}
ExpectToken(Token::kIN);
@ -9411,6 +9438,7 @@ AstNode* Parser::ParseForInStatement(TokenPosition forin_pos,
// until the loop variable is assigned to.
const AbstractType& iterator_type = Object::dynamic_type();
LocalVariable* iterator_var = new(Z) LocalVariable(
collection_pos,
collection_pos, Symbols::ForInIter(), iterator_type);
current_block_->scope->AddVariable(iterator_var);
@ -9448,7 +9476,8 @@ AstNode* Parser::ParseForInStatement(TokenPosition forin_pos,
// The for loop variable is new for each iteration.
// Create a variable and add it to the loop body scope.
LocalVariable* loop_var =
new(Z) LocalVariable(loop_var_assignment_pos,
new(Z) LocalVariable(loop_var_pos,
loop_var_assignment_pos,
*loop_var_name,
loop_var_type);;
if (loop_var_is_final) {
@ -9614,6 +9643,7 @@ void Parser::AddCatchParamsToScope(CatchParamDesc* exception_param,
LocalScope* scope) {
if (exception_param->name != NULL) {
LocalVariable* var = new(Z) LocalVariable(
exception_param->token_pos,
exception_param->token_pos,
*exception_param->name,
*exception_param->type);
@ -9624,6 +9654,7 @@ void Parser::AddCatchParamsToScope(CatchParamDesc* exception_param,
}
if (stack_trace_param->name != NULL) {
LocalVariable* var = new(Z) LocalVariable(
stack_trace_param->token_pos,
stack_trace_param->token_pos,
*stack_trace_param->name,
*stack_trace_param->type);
@ -10033,6 +10064,7 @@ void Parser::SetupSavedTryContext(LocalVariable* saved_try_context) {
Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
last_used_try_index_ - 1));
LocalVariable* async_saved_try_ctx = new (Z) LocalVariable(
TokenPosition::kNoSource,
TokenPosition::kNoSource,
async_saved_try_ctx_name,
Object::dynamic_type());
@ -10074,6 +10106,7 @@ void Parser::SetupExceptionVariables(LocalScope* try_scope,
*context_var = try_scope->LocalLookupVariable(Symbols::SavedTryContextVar());
if (*context_var == NULL) {
*context_var = new(Z) LocalVariable(
TokenPos(),
TokenPos(),
Symbols::SavedTryContextVar(),
Object::dynamic_type());
@ -10082,6 +10115,7 @@ void Parser::SetupExceptionVariables(LocalScope* try_scope,
*exception_var = try_scope->LocalLookupVariable(Symbols::ExceptionVar());
if (*exception_var == NULL) {
*exception_var = new(Z) LocalVariable(
TokenPos(),
TokenPos(),
Symbols::ExceptionVar(),
Object::dynamic_type());
@ -10090,6 +10124,7 @@ void Parser::SetupExceptionVariables(LocalScope* try_scope,
*stack_trace_var = try_scope->LocalLookupVariable(Symbols::StackTraceVar());
if (*stack_trace_var == NULL) {
*stack_trace_var = new(Z) LocalVariable(
TokenPos(),
TokenPos(),
Symbols::StackTraceVar(),
Object::dynamic_type());
@ -10100,6 +10135,7 @@ void Parser::SetupExceptionVariables(LocalScope* try_scope,
Symbols::SavedExceptionVar());
if (*saved_exception_var == NULL) {
*saved_exception_var = new(Z) LocalVariable(
TokenPos(),
TokenPos(),
Symbols::SavedExceptionVar(),
Object::dynamic_type());
@ -10109,6 +10145,7 @@ void Parser::SetupExceptionVariables(LocalScope* try_scope,
Symbols::SavedStackTraceVar());
if (*saved_stack_trace_var == NULL) {
*saved_stack_trace_var = new(Z) LocalVariable(
TokenPos(),
TokenPos(),
Symbols::SavedStackTraceVar(),
Object::dynamic_type());
@ -11089,6 +11126,7 @@ LocalVariable* Parser::CreateTempConstVariable(TokenPosition token_pos,
char name[64];
OS::SNPrint(name, 64, ":%s%" Pd "", s, token_pos.value());
LocalVariable* temp = new(Z) LocalVariable(
token_pos,
token_pos,
String::ZoneHandle(Z, Symbols::New(T, name)),
Object::dynamic_type());

View file

@ -114,6 +114,7 @@ class ParsedFunction : public ZoneAllocated {
ASSERT(function.IsZoneHandle());
// Every function has a local variable for the current context.
LocalVariable* temp = new(zone()) LocalVariable(
function.token_pos(),
function.token_pos(),
Symbols::CurrentContextVar(),
Object::dynamic_type());

View file

@ -1355,6 +1355,7 @@ class RawLocalVarDescriptors : public RawObject {
struct VarInfo {
int32_t index_kind; // Bitfield for slot index on stack or in context,
// and Entry kind of type VarInfoKind.
TokenPosition declaration_pos; // Token position of declaration.
TokenPosition begin_pos; // Token position of scope start.
TokenPosition end_pos; // Token position of scope end.
int16_t scope_id; // Scope to which the variable belongs.
@ -1457,6 +1458,7 @@ class RawContextScope : public RawObject {
// TODO(iposva): Switch to conventional enum offset based structure to avoid
// alignment mishaps.
struct VariableDesc {
RawSmi* declaration_token_pos;
RawSmi* token_pos;
RawString* name;
RawBool* is_final;

View file

@ -1485,6 +1485,7 @@ RawContextScope* ContextScope::ReadFrom(SnapshotReader* reader,
// Create a descriptor for 'this' variable.
context_scope.SetTokenIndexAt(0, TokenPosition::kMinSource);
context_scope.SetDeclarationTokenIndexAt(0, TokenPosition::kMinSource);
context_scope.SetNameAt(0, Symbols::This());
context_scope.SetIsFinalAt(0, true);
context_scope.SetIsConstAt(0, false);

View file

@ -385,7 +385,10 @@ DEFINE_RAW_LEAF_RUNTIME_ENTRY(
LocalVariable* IRRegExpMacroAssembler::Parameter(const String& name,
intptr_t index) const {
LocalVariable* local = new(Z) LocalVariable(
TokenPosition::kNoSource, name, Object::dynamic_type());
TokenPosition::kNoSource,
TokenPosition::kNoSource,
name,
Object::dynamic_type());
intptr_t param_frame_index = kParamEndSlotFromFp + kParamCount - index;
local->set_index(param_frame_index);
@ -396,7 +399,10 @@ LocalVariable* IRRegExpMacroAssembler::Parameter(const String& name,
LocalVariable* IRRegExpMacroAssembler::Local(const String& name) {
LocalVariable* local = new(Z) LocalVariable(
TokenPosition::kNoSource, name, Object::dynamic_type());
TokenPosition::kNoSource,
TokenPosition::kNoSource,
name,
Object::dynamic_type());
local->set_index(GetNextLocalIndex());
return local;

View file

@ -288,6 +288,7 @@ RawLocalVarDescriptors* LocalScope::GetVarDescriptors(const Function& func) {
desc.name = &name;
desc.info.set_kind(kind);
desc.info.scope_id = context_scope.ContextLevelAt(i);
desc.info.declaration_pos = context_scope.DeclarationTokenIndexAt(i);
desc.info.begin_pos = begin_token_pos();
desc.info.end_pos = end_token_pos();
ASSERT(desc.info.begin_pos <= desc.info.end_pos);
@ -336,6 +337,7 @@ void LocalScope::CollectLocalVariables(GrowableArray<VarDesc>* vars,
desc.name = &var->name();
desc.info.set_kind(RawLocalVarDescriptors::kSavedCurrentContext);
desc.info.scope_id = 0;
desc.info.declaration_pos = TokenPosition::kMinSource;
desc.info.begin_pos = TokenPosition::kMinSource;
desc.info.end_pos = TokenPosition::kMinSource;
desc.info.set_index(var->index());
@ -353,6 +355,7 @@ void LocalScope::CollectLocalVariables(GrowableArray<VarDesc>* vars,
desc.info.set_kind(RawLocalVarDescriptors::kStackVar);
desc.info.scope_id = *scope_id;
}
desc.info.declaration_pos = var->declaration_token_pos();
desc.info.begin_pos = var->token_pos();
desc.info.end_pos = var->owner()->end_token_pos();
desc.info.set_index(var->index());
@ -551,6 +554,8 @@ RawContextScope* LocalScope::PreserveOuterScope(int current_context_level)
// Preserve the aliases of captured variables belonging to outer scopes.
if (variable->owner()->function_level() != 1) {
context_scope.SetTokenIndexAt(captured_idx, variable->token_pos());
context_scope.SetDeclarationTokenIndexAt(
captured_idx, variable->declaration_token_pos());
context_scope.SetNameAt(captured_idx, variable->name());
context_scope.SetIsFinalAt(captured_idx, variable->is_final());
context_scope.SetIsConstAt(captured_idx, variable->IsConst());
@ -582,13 +587,17 @@ LocalScope* LocalScope::RestoreOuterScope(const ContextScope& context_scope) {
for (int i = 0; i < context_scope.num_variables(); i++) {
LocalVariable* variable;
if (context_scope.IsConstAt(i)) {
variable = new LocalVariable(context_scope.TokenIndexAt(i),
variable = new LocalVariable(
context_scope.DeclarationTokenIndexAt(i),
context_scope.TokenIndexAt(i),
String::ZoneHandle(context_scope.NameAt(i)),
Object::dynamic_type());
variable->SetConstValue(
Instance::ZoneHandle(context_scope.ConstValueAt(i)));
} else {
variable = new LocalVariable(context_scope.TokenIndexAt(i),
variable = new LocalVariable(
context_scope.DeclarationTokenIndexAt(i),
context_scope.TokenIndexAt(i),
String::ZoneHandle(context_scope.NameAt(i)),
AbstractType::ZoneHandle(context_scope.TypeAt(i)));
}
@ -640,6 +649,7 @@ RawContextScope* LocalScope::CreateImplicitClosureScope(const Function& func) {
// Create a descriptor for 'this' variable.
context_scope.SetTokenIndexAt(0, func.token_pos());
context_scope.SetDeclarationTokenIndexAt(0, func.token_pos());
context_scope.SetNameAt(0, Symbols::This());
context_scope.SetIsFinalAt(0, true);
context_scope.SetIsConstAt(0, false);

View file

@ -21,10 +21,12 @@ class LocalScope;
class LocalVariable : public ZoneAllocated {
public:
LocalVariable(TokenPosition token_pos,
LocalVariable(TokenPosition declaration_pos,
TokenPosition token_pos,
const String& name,
const AbstractType& type)
: token_pos_(token_pos),
: declaration_pos_(declaration_pos),
token_pos_(token_pos),
name_(name),
owner_(NULL),
type_(type),
@ -41,6 +43,7 @@ class LocalVariable : public ZoneAllocated {
}
TokenPosition token_pos() const { return token_pos_; }
TokenPosition declaration_token_pos() const { return declaration_pos_; }
const String& name() const { return name_; }
LocalScope* owner() const { return owner_; }
void set_owner(LocalScope* owner) {
@ -117,6 +120,7 @@ class LocalVariable : public ZoneAllocated {
private:
static const int kUninitializedIndex = INT_MIN;
const TokenPosition declaration_pos_;
const TokenPosition token_pos_;
const String& name_;
LocalScope* owner_; // Local scope declaring this variable.

View file

@ -14,15 +14,19 @@ TEST_CASE(LocalScope) {
const Type& dynamic_type = Type::ZoneHandle(Type::DynamicType());
const String& a = String::ZoneHandle(Symbols::New(thread, "a"));
LocalVariable* var_a =
new LocalVariable(TokenPosition::kNoSource, a, dynamic_type);
new LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource, a, dynamic_type);
LocalVariable* inner_var_a =
new LocalVariable(TokenPosition::kNoSource, a, dynamic_type);
new LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource, a, dynamic_type);
const String& b = String::ZoneHandle(Symbols::New(thread, "b"));
LocalVariable* var_b =
new LocalVariable(TokenPosition::kNoSource, b, dynamic_type);
new LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource, b, dynamic_type);
const String& c = String::ZoneHandle(Symbols::New(thread, "c"));
LocalVariable* var_c =
new LocalVariable(TokenPosition::kNoSource, c, dynamic_type);
new LocalVariable(TokenPosition::kNoSource,
TokenPosition::kNoSource, c, dynamic_type);
const String& L = String::ZoneHandle(Symbols::New(thread, "L"));
SourceLabel* label_L =
new SourceLabel(TokenPosition::kNoSource, L, SourceLabel::kFor);

View file

@ -895,6 +895,15 @@ _BeingInitialized_ [Sentinel](#sentinel).
class BoundVariable {
string name;
@Instance|Sentinel value;
// The token position where this variable was declared.
int declarationTokenPos;
// The first token position where this variable is visible to the scope.
int scopeStartTokenPos;
// The last token position where this variable is visible to the scope.
int scopeEndTokenPos;
}
```
@ -2528,4 +2537,6 @@ version | comments
3.3 | Pause event now indicates if the isolate is paused at an await, yield, or yield* suspension point via the 'atAsyncSuspension' field. Resume command now supports the step parameter 'OverAsyncSuspension'. A Breakpoint added synthetically by an 'OverAsyncSuspension' resume command identifies itself as such via the 'isSyntheticAsyncContinuation' field.
3.4 | Add the superType and mixin fields to Class. Added new pause event 'None'.
3.5 | Add the error field to SourceReportRange. Clarify definition of token position. Add "Isolate must be paused" error code.
3.6 (unreleased) | Add 'scopeStartTokenPos', 'scopeEndTokenPos', and 'declarationTokenPos' to BoundVariable.
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss