Files
sdk/runtime/vm/flow_graph_builder_test.cc
T
Matthias Hausner 408a64756e Fix setting breakpoint setting in await statements
CL https://codereview.chromium.org/2626753002 introduced a “real” token
position for the synthetic code that re-throws an exception returned
by an await’ed expression. This interferes with setting a breakpoint
in a line that contains an await, since the synthetic code happens to
be at the lowest compiled code address and will thus be picked as the
breakpoint location.

This CL makes the re-throw a synthetic token position again, but
includes synthetic token positions in stack traces. This is an alternative
fix for bug #28325.

BUG=#28770
R=fschneider@google.com

Review-Url: https://codereview.chromium.org/2727803002 .
2017-03-02 12:21:42 -08:00

791 lines
26 KiB
C++

// 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.
#include "vm/compiler.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
#include "vm/flow_graph_builder.h"
#include "vm/intermediate_language.h"
#include "vm/unit_test.h"
namespace dart {
#ifndef PRODUCT
#define DUMP_ASSERT(condition) \
if (!(condition)) { \
dart::Expect(__FILE__, __LINE__).Fail("expected: %s", #condition); \
THR_Print(">>> BEGIN source position table for `%s`\n", graph_name_); \
Dump(); \
THR_Print("<<< END source position table for `%s`\n", graph_name_); \
OS::Abort(); \
}
class SourcePositionTest : public ValueObject {
public:
SourcePositionTest(Thread* thread, const char* script)
: thread_(thread),
isolate_(thread->isolate()),
script_(script),
root_lib_(Library::Handle()),
root_script_(Script::Handle()),
graph_(NULL),
blocks_(NULL) {
EXPECT(thread_ != NULL);
EXPECT(isolate_ != NULL);
EXPECT(script_ != NULL);
Dart_Handle lib = TestCase::LoadTestScript(script, NULL);
EXPECT_VALID(lib);
root_lib_ ^= Api::UnwrapHandle(lib);
EXPECT(!root_lib_.IsNull());
root_script_ ^=
root_lib_.LookupScript(String::Handle(String::New(USER_TEST_URI)));
EXPECT(!root_script_.IsNull());
}
void BuildGraphFor(const char* function_name) {
graph_ = NULL;
blocks_ = NULL;
graph_name_ = NULL;
// Only support unoptimized code for now.
const bool optimized = false;
const Function& function =
Function::Handle(GetFunction(root_lib_, function_name));
ZoneGrowableArray<const ICData*>* ic_data_array =
new ZoneGrowableArray<const ICData*>();
ParsedFunction* parsed_function =
new ParsedFunction(thread_, Function::ZoneHandle(function.raw()));
Parser::ParseFunction(parsed_function);
parsed_function->AllocateVariables();
FlowGraphBuilder builder(*parsed_function, *ic_data_array, NULL,
Compiler::kNoOSRDeoptId);
graph_ = builder.BuildGraph();
EXPECT(graph_ != NULL);
blocks_ = graph_->CodegenBlockOrder(optimized);
EXPECT(blocks_ != NULL);
graph_name_ = function_name;
EXPECT(graph_name_ != NULL);
}
// Expect to find an instance call at |line| and |column|.
void InstanceCallAt(intptr_t line,
intptr_t column = -1,
Token::Kind kind = Token::kNumTokens) {
ZoneGrowableArray<Instruction*>* instructions =
FindInstructionsAt(line, column);
intptr_t count = 0;
for (intptr_t i = 0; i < instructions->length(); i++) {
Instruction* instr = instructions->At(i);
EXPECT(instr != NULL);
if (instr->IsInstanceCall()) {
if (kind != Token::kNumTokens) {
if (instr->AsInstanceCall()->token_kind() == kind) {
count++;
}
} else {
count++;
}
}
}
DUMP_ASSERT(count > 0);
}
// Expect to find an instance call at |line| and |column|.
void InstanceCallAt(const char* needle, intptr_t line, intptr_t column = -1) {
ZoneGrowableArray<Instruction*>* instructions =
FindInstructionsAt(line, column);
intptr_t count = 0;
for (intptr_t i = 0; i < instructions->length(); i++) {
Instruction* instr = instructions->At(i);
EXPECT(instr != NULL);
if (instr->IsInstanceCall()) {
const char* haystack = instr->ToCString();
if (strstr(haystack, needle) != NULL) {
count++;
}
}
}
DUMP_ASSERT(count > 0);
}
// Expect to find at least one static call at |line| and |column|. The
// static call will have |needle| in its |ToCString| representation.
void StaticCallAt(const char* needle, intptr_t line, intptr_t column = -1) {
ZoneGrowableArray<Instruction*>* instructions =
FindInstructionsAt(line, column);
intptr_t count = 0;
for (intptr_t i = 0; i < instructions->length(); i++) {
Instruction* instr = instructions->At(i);
EXPECT(instr != NULL);
if (instr->IsStaticCall()) {
const char* haystack = instr->ToCString();
if (strstr(haystack, needle) != NULL) {
count++;
}
}
}
DUMP_ASSERT(count > 0);
}
// Expect that at least one of the instructions found at |line| and |column|
// contain |needle| in their |ToCString| representation.
void FuzzyInstructionMatchAt(const char* needle,
intptr_t line,
intptr_t column = -1) {
ZoneGrowableArray<Instruction*>* instructions =
FindInstructionsAt(line, column);
intptr_t count = 0;
for (intptr_t i = 0; i < instructions->length(); i++) {
Instruction* instr = instructions->At(i);
const char* haystack = instr->ToCString();
if (strstr(haystack, needle) != NULL) {
count++;
}
}
DUMP_ASSERT(count > 0);
}
// Utility to dump the instructions with token positions or line numbers.
void Dump() {
for (intptr_t i = 0; i < blocks_->length(); i++) {
BlockEntryInstr* entry = (*blocks_)[i];
THR_Print("B%" Pd ":\n", entry->block_id());
DumpInstruction(entry);
for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
DumpInstruction(it.Current());
}
}
}
// Fails if any of the IR nodes has a token position of
// TokenPosition::kNoSourcePos.
void EnsureSourcePositions() {
for (intptr_t i = 0; i < blocks_->length(); i++) {
BlockEntryInstr* entry = (*blocks_)[i];
DUMP_ASSERT(entry->token_pos() != TokenPosition::kNoSource);
for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
Instruction* instr = it.Current();
DUMP_ASSERT(instr->token_pos() != TokenPosition::kNoSource);
}
}
}
private:
void DumpInstruction(Instruction* instr) {
TokenPosition token_pos = instr->token_pos();
bool synthetic = false;
if (token_pos.IsSynthetic()) {
synthetic = true;
token_pos = token_pos.FromSynthetic();
}
if (token_pos.IsClassifying()) {
const char* token_pos_string = token_pos.ToCString();
THR_Print("%12s -- %s\n", token_pos_string, instr->ToCString());
return;
}
intptr_t token_line = -1;
intptr_t token_column = -1;
root_script_.GetTokenLocation(token_pos, &token_line, &token_column, NULL);
if (synthetic) {
THR_Print(" *%02d:%02d -- %s\n", static_cast<int>(token_line),
static_cast<int>(token_column), instr->ToCString());
} else {
THR_Print(" %02d:%02d -- %s\n", static_cast<int>(token_line),
static_cast<int>(token_column), instr->ToCString());
}
}
Instruction* FindFirstInstructionAt(intptr_t line, intptr_t column) {
ZoneGrowableArray<Instruction*>* instructions =
FindInstructionsAt(line, column);
if (instructions->length() == 0) {
return NULL;
}
return instructions->At(0);
}
ZoneGrowableArray<Instruction*>* FindInstructionsAt(intptr_t line,
intptr_t column) {
ZoneGrowableArray<Instruction*>* instructions =
new ZoneGrowableArray<Instruction*>();
for (intptr_t i = 0; i < blocks_->length(); i++) {
BlockEntryInstr* entry = (*blocks_)[i];
for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
Instruction* instr = it.Current();
const TokenPosition token_pos = instr->token_pos().SourcePosition();
if (!token_pos.IsReal()) {
continue;
}
intptr_t token_line = -1;
intptr_t token_column = -1;
root_script_.GetTokenLocation(token_pos, &token_line, &token_column,
NULL);
if (token_line == line) {
if ((column < 0) || (column == token_column)) {
instructions->Add(instr);
}
}
}
}
return instructions;
}
ZoneGrowableArray<Instruction*>* FindInstructionsAt(intptr_t token_pos) {
ZoneGrowableArray<Instruction*>* instructions =
new ZoneGrowableArray<Instruction*>();
for (intptr_t i = 0; i < blocks_->length(); i++) {
BlockEntryInstr* entry = (*blocks_)[i];
for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
Instruction* instr = it.Current();
if (instr->token_pos().value() == token_pos) {
instructions->Add(instr);
}
}
}
return instructions;
}
RawFunction* GetFunction(const Library& lib, const char* name) {
const Function& result = Function::Handle(
lib.LookupFunctionAllowPrivate(String::Handle(String::New(name))));
EXPECT(!result.IsNull());
return result.raw();
}
RawFunction* GetFunction(const Class& cls, const char* name) {
const Function& result = Function::Handle(
cls.LookupFunctionAllowPrivate(String::Handle(String::New(name))));
EXPECT(!result.IsNull());
return result.raw();
}
RawClass* GetClass(const Library& lib, const char* name) {
const Class& cls = Class::Handle(
lib.LookupClass(String::Handle(Symbols::New(thread_, name))));
EXPECT(!cls.IsNull()); // No ambiguity error expected.
return cls.raw();
}
Thread* thread_;
Isolate* isolate_;
const char* script_;
Library& root_lib_;
Script& root_script_;
const char* graph_name_;
FlowGraph* graph_;
GrowableArray<BlockEntryInstr*>* blocks_;
};
TEST_CASE(SourcePosition_InstanceCalls) {
const char* kScript =
"var x = 5;\n"
"var y = 5;\n"
"main() {\n"
" var z = x + y;\n"
" return z;\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.InstanceCallAt(4, 13, Token::kADD);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 5, 3);
spt.FuzzyInstructionMatchAt("Return", 5, 3);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_If) {
const char* kScript =
"var x = 5;\n"
"var y = 5;\n"
"main() {\n"
" if (x != 0) {\n"
" return x;\n"
" }\n"
" return y;\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.FuzzyInstructionMatchAt("LoadStaticField", 4, 7);
spt.InstanceCallAt(4, 9, Token::kEQ);
spt.FuzzyInstructionMatchAt("Branch if StrictCompare", 4, 9);
spt.FuzzyInstructionMatchAt("LoadStaticField", 5, 12);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 5, 5);
spt.FuzzyInstructionMatchAt("Return", 5, 5);
spt.FuzzyInstructionMatchAt("LoadStaticField", 7, 10);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 7, 3);
spt.FuzzyInstructionMatchAt("Return", 7, 3);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_ForLoop) {
const char* kScript =
"var x = 0;\n"
"var y = 5;\n"
"main() {\n"
" for (var i = 0; i < 10; i++) {\n"
" x += i;\n"
" }\n"
" return x;\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.FuzzyInstructionMatchAt("StoreLocal", 4, 14);
spt.FuzzyInstructionMatchAt("LoadLocal", 4, 19);
spt.InstanceCallAt(4, 21, Token::kLT);
spt.FuzzyInstructionMatchAt("Branch if StrictCompare", 4, 21);
spt.FuzzyInstructionMatchAt("LoadStaticField", 5, 5);
spt.FuzzyInstructionMatchAt("StoreStaticField", 5, 5);
spt.InstanceCallAt(5, 7, Token::kADD);
spt.FuzzyInstructionMatchAt("LoadLocal", 5, 10);
spt.FuzzyInstructionMatchAt("LoadStaticField", 7, 10);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 7, 3);
spt.FuzzyInstructionMatchAt("Return", 7, 3);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_While) {
const char* kScript =
"var x = 0;\n"
"var y = 5;\n"
"main() {\n"
" while (x < 10) {\n"
" if (y == 5) {\n"
" return y;\n"
" }\n"
" x++;\n"
" }\n"
" return x;\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 4, 3);
spt.FuzzyInstructionMatchAt("Constant", 4, 10);
spt.FuzzyInstructionMatchAt("LoadStaticField", 4, 10);
spt.InstanceCallAt(4, 12, Token::kLT);
spt.FuzzyInstructionMatchAt("Branch if StrictCompare", 4, 12);
spt.FuzzyInstructionMatchAt("Constant", 5, 9);
spt.FuzzyInstructionMatchAt("LoadStaticField", 5, 9);
spt.InstanceCallAt(5, 11, Token::kEQ);
spt.FuzzyInstructionMatchAt("Branch if StrictCompare", 5, 11);
spt.FuzzyInstructionMatchAt("Constant", 6, 14);
spt.FuzzyInstructionMatchAt("LoadStaticField", 6, 14);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 6, 7);
spt.FuzzyInstructionMatchAt("Return", 6, 7);
spt.FuzzyInstructionMatchAt("Constant", 8, 5);
spt.FuzzyInstructionMatchAt("LoadStaticField", 8, 5);
spt.FuzzyInstructionMatchAt("Constant(#1)", 8, 6);
spt.InstanceCallAt(8, 6, Token::kADD);
spt.FuzzyInstructionMatchAt("StoreStaticField", 8, 5);
spt.FuzzyInstructionMatchAt("LoadStaticField", 10, 10);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 10, 3);
spt.FuzzyInstructionMatchAt("Return", 10, 3);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_WhileContinueBreak) {
const char* kScript =
"var x = 0;\n"
"var y = 5;\n"
"main() {\n"
" while (x < 10) {\n"
" if (y == 5) {\n"
" continue;\n"
" }\n"
" break;\n"
" }\n"
" return x;\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 4, 3);
spt.FuzzyInstructionMatchAt("Constant(#Field", 4, 10);
spt.FuzzyInstructionMatchAt("LoadStaticField", 4, 10);
spt.FuzzyInstructionMatchAt("Constant(#10", 4, 14);
spt.InstanceCallAt(4, 12, Token::kLT);
spt.FuzzyInstructionMatchAt("Branch if StrictCompare", 4, 12);
spt.FuzzyInstructionMatchAt("Constant(#Field", 5, 9);
spt.FuzzyInstructionMatchAt("LoadStaticField", 5, 9);
spt.FuzzyInstructionMatchAt("Constant(#5", 5, 14);
spt.InstanceCallAt(5, 11, Token::kEQ);
spt.FuzzyInstructionMatchAt("Branch if StrictCompare", 5, 11);
spt.FuzzyInstructionMatchAt("LoadStaticField", 10, 10);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 10, 3);
spt.FuzzyInstructionMatchAt("Return", 10, 3);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_LoadIndexed) {
const char* kScript =
"var x = 0;\n"
"var z = new List(3);\n"
"main() {\n"
" z[0];\n"
" var y = z[0] + z[1] + z[2];\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.StaticCallAt("get:z", 4, 3);
spt.FuzzyInstructionMatchAt("Constant(#0)", 4, 5);
spt.InstanceCallAt(4, 4, Token::kINDEX);
spt.FuzzyInstructionMatchAt("Constant(#0)", 5, 13);
spt.InstanceCallAt(5, 12, Token::kINDEX);
spt.FuzzyInstructionMatchAt("Constant(#1)", 5, 20);
spt.InstanceCallAt(5, 19, Token::kINDEX);
spt.InstanceCallAt(5, 16, Token::kADD);
spt.StaticCallAt("get:z", 5, 25);
spt.FuzzyInstructionMatchAt("Constant(#2)", 5, 27);
spt.InstanceCallAt(5, 26, Token::kINDEX);
spt.InstanceCallAt(5, 23, Token::kADD);
spt.FuzzyInstructionMatchAt("Constant(#null)", 6, 1);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 6, 1);
spt.FuzzyInstructionMatchAt("Return", 6, 1);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_StoreIndexed) {
const char* kScript =
"var x = 0;\n"
"var z = new List(4);\n"
"main() {\n"
" z[0];\n"
" z[3] = z[0] + z[1] + z[2];\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.StaticCallAt("get:z", 4, 3);
spt.FuzzyInstructionMatchAt("Constant(#0)", 4, 5);
spt.InstanceCallAt(4, 4, Token::kINDEX);
spt.FuzzyInstructionMatchAt("Constant(#3)", 5, 5);
spt.StaticCallAt("get:z", 5, 10);
spt.FuzzyInstructionMatchAt("Constant(#0)", 5, 12);
spt.InstanceCallAt(5, 11, Token::kINDEX);
spt.InstanceCallAt(5, 15, Token::kADD);
spt.StaticCallAt("get:z", 5, 17);
spt.FuzzyInstructionMatchAt("Constant(#1)", 5, 19);
spt.InstanceCallAt(5, 18, Token::kINDEX);
spt.StaticCallAt("get:z", 5, 24);
spt.FuzzyInstructionMatchAt("Constant(#2)", 5, 26);
spt.InstanceCallAt(5, 25, Token::kINDEX);
spt.InstanceCallAt(5, 4, Token::kASSIGN_INDEX);
spt.FuzzyInstructionMatchAt("Constant(#null)", 6, 1);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 6, 1);
spt.FuzzyInstructionMatchAt("Return", 6, 1);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_BitwiseOperations) {
const char* kScript =
"var x = 0;\n"
"var y = 1;\n"
"main() {\n"
" var z;\n"
" z = x & y;\n"
" z = x | y;\n"
" z = x ^ y;\n"
" z = ~z;\n"
" return z;\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 4, 7);
spt.FuzzyInstructionMatchAt("Constant(#null", 4, 7);
spt.FuzzyInstructionMatchAt("StoreLocal(z", 4, 7);
spt.FuzzyInstructionMatchAt("LoadStaticField", 5, 7);
spt.FuzzyInstructionMatchAt("LoadStaticField", 5, 11);
spt.InstanceCallAt(5, 9, Token::kBIT_AND);
spt.FuzzyInstructionMatchAt("StoreLocal(z", 5, 3);
spt.FuzzyInstructionMatchAt("LoadStaticField", 6, 7);
spt.FuzzyInstructionMatchAt("LoadStaticField", 6, 11);
spt.InstanceCallAt(6, 9, Token::kBIT_OR);
spt.FuzzyInstructionMatchAt("StoreLocal(z", 6, 3);
spt.FuzzyInstructionMatchAt("LoadStaticField", 7, 7);
spt.FuzzyInstructionMatchAt("LoadStaticField", 7, 11);
spt.InstanceCallAt(7, 9, Token::kBIT_XOR);
spt.FuzzyInstructionMatchAt("StoreLocal(z", 7, 3);
spt.FuzzyInstructionMatchAt("LoadLocal(z", 8, 8);
spt.InstanceCallAt(8, 7, Token::kBIT_NOT);
spt.FuzzyInstructionMatchAt("StoreLocal(z", 8, 3);
spt.FuzzyInstructionMatchAt("LoadLocal(z", 9, 10);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 9, 3);
spt.FuzzyInstructionMatchAt("Return", 9, 3);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_IfElse) {
const char* kScript =
"var x = 5;\n"
"var y = 5;\n"
"main() {\n"
" if (x != 0) {\n"
" return x;\n"
" } else {\n"
" return y;\n"
" }\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.FuzzyInstructionMatchAt("LoadStaticField", 4, 7);
spt.InstanceCallAt(4, 9, Token::kEQ);
spt.FuzzyInstructionMatchAt("Branch if StrictCompare", 4, 9);
spt.FuzzyInstructionMatchAt("LoadStaticField", 5, 12);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 5, 5);
spt.FuzzyInstructionMatchAt("Return", 5, 5);
spt.FuzzyInstructionMatchAt("LoadStaticField", 7, 12);
spt.FuzzyInstructionMatchAt("DebugStepCheck", 7, 5);
spt.FuzzyInstructionMatchAt("Return", 7, 5);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_Switch) {
const char* kScript =
"var x = 5;\n"
"var y = 5;\n"
"main() {\n"
" switch (x) {\n"
" case 1: return 3;\n"
" case 2: return 4;\n"
" default: return 5;\n"
" }\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.FuzzyInstructionMatchAt("Constant(#Field", 4, 11);
spt.FuzzyInstructionMatchAt("LoadStaticField", 4, 11);
spt.FuzzyInstructionMatchAt("StoreLocal(:switch_expr", 4, 11);
spt.FuzzyInstructionMatchAt("Constant(#1", 5, 10);
spt.FuzzyInstructionMatchAt("LoadLocal(:switch_expr", 5, 5); // 'c'
spt.InstanceCallAt(5, 10, Token::kEQ); // '1'
spt.FuzzyInstructionMatchAt("Constant(#3", 5, 20); // '3'
spt.FuzzyInstructionMatchAt("DebugStepCheck", 5, 13);
spt.FuzzyInstructionMatchAt("Return", 5, 13);
spt.FuzzyInstructionMatchAt("Constant(#2", 6, 10);
spt.FuzzyInstructionMatchAt("LoadLocal(:switch_expr", 6, 5); // 'c'
spt.InstanceCallAt(6, 10, Token::kEQ); // '2'
spt.FuzzyInstructionMatchAt("Constant(#4", 6, 20); // '4'
spt.FuzzyInstructionMatchAt("DebugStepCheck", 6, 13);
spt.FuzzyInstructionMatchAt("Return", 6, 13);
spt.FuzzyInstructionMatchAt("Constant(#5", 7, 21); // '5'
spt.FuzzyInstructionMatchAt("DebugStepCheck", 7, 14);
spt.FuzzyInstructionMatchAt("Return", 7, 14);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_TryCatchFinally) {
const char* kScript =
"var x = 5;\n"
"var y = 5;\n"
"main() {\n"
" try {\n"
" throw 'A';\n"
" } catch (e) {\n"
" print(e);\n"
" return 77;\n"
" } finally {\n"
" return 99;\n"
" }\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("DebugStepCheck", 3, 5);
spt.FuzzyInstructionMatchAt("CheckStackOverflow", 3, 5);
spt.FuzzyInstructionMatchAt("LoadLocal(:current_context", 4, 3); // 't'
spt.FuzzyInstructionMatchAt("StoreLocal(:saved_try_context", 4, 3);
spt.FuzzyInstructionMatchAt("Constant(#A", 5, 11); // 'A'
spt.FuzzyInstructionMatchAt("Throw", 5, 5); // 't'
spt.FuzzyInstructionMatchAt("LoadLocal(:saved_try_context", 6, 5); // 'c'
spt.FuzzyInstructionMatchAt("StoreLocal(:current_context", 6, 5); // 'c'
spt.FuzzyInstructionMatchAt("LoadLocal(:exception_var", 6, 5); // 'c'
spt.FuzzyInstructionMatchAt("StoreLocal(e", 6, 5); // 'c'
spt.FuzzyInstructionMatchAt("LoadLocal(e", 7, 11); // 'e'
spt.FuzzyInstructionMatchAt("StaticCall", 7, 5); // 'p'
spt.FuzzyInstructionMatchAt("Constant(#77", 8, 12); // '7'
spt.FuzzyInstructionMatchAt("StoreLocal(:finally_ret_val", 8, 5); // 'r'
spt.FuzzyInstructionMatchAt("Constant(#99", 10, 12); // '9'
spt.FuzzyInstructionMatchAt("Return", 10, 5); // 'r'
spt.FuzzyInstructionMatchAt("LoadLocal(:saved_try_context", 9, 13); // '{'
spt.FuzzyInstructionMatchAt("StoreLocal(:current_context", 9, 13); // '{'
spt.FuzzyInstructionMatchAt("Constant(#99", 10, 12); // '9'
spt.FuzzyInstructionMatchAt("Return", 10, 5); // 'r'
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_InstanceFields) {
const char* kScript =
"class A {\n"
" var x;\n"
" var y;\n"
"}\n"
"main() {\n"
" var z = new A();\n"
" z.x = 99;\n"
" z.y = z.x;\n"
" return z.y;\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("main");
spt.FuzzyInstructionMatchAt("AllocateObject(A)", 6, 15); // 'A'
spt.FuzzyInstructionMatchAt("StaticCall", 6, 15); // 'A'
spt.FuzzyInstructionMatchAt("StoreLocal(z", 6, 9); // '='
spt.InstanceCallAt("set:x", 7, 5); // 'x'
spt.InstanceCallAt("get:x", 8, 11); // 'x'
spt.InstanceCallAt("set:y", 8, 5); // 'y'
spt.InstanceCallAt("get:y", 9, 12); // 'y'
spt.FuzzyInstructionMatchAt("DebugStepCheck", 9, 3);
spt.FuzzyInstructionMatchAt("Return", 9, 3);
spt.EnsureSourcePositions();
}
TEST_CASE(SourcePosition_Async) {
const char* kScript =
"import 'dart:async';\n"
"var x = 5;\n"
"var y = 5;\n"
"foo(Future f1, Future f2) async {\n"
" await f1;\n"
" await f2;\n"
" return 55;\n"
"}\n"
"main() {\n"
" foo(new Future.value(33));\n"
"}\n";
SourcePositionTest spt(thread, kScript);
spt.BuildGraphFor("foo");
spt.EnsureSourcePositions();
spt.Dump();
}
#endif // !PRODUCT
static bool SyntheticRoundTripTest(TokenPosition token_pos) {
const TokenPosition synthetic_token_pos = token_pos.ToSynthetic();
return synthetic_token_pos.FromSynthetic() == token_pos;
}
VM_UNIT_TEST_CASE(SourcePosition_SyntheticTokens) {
EXPECT(TokenPosition::kNoSourcePos == -1);
EXPECT(TokenPosition::kMinSourcePos == 0);
EXPECT(TokenPosition::kMaxSourcePos > 0);
EXPECT(TokenPosition::kMaxSourcePos > TokenPosition::kMinSourcePos);
EXPECT(TokenPosition::kMinSource.value() == TokenPosition::kMinSourcePos);
EXPECT(TokenPosition::kMaxSource.value() == TokenPosition::kMaxSourcePos);
EXPECT(!TokenPosition(0).IsSynthetic());
EXPECT(TokenPosition(0).ToSynthetic().IsSynthetic());
EXPECT(TokenPosition(9).ToSynthetic().IsSynthetic());
EXPECT(!TokenPosition(-1).FromSynthetic().IsSynthetic());
EXPECT(!TokenPosition::kNoSource.IsSynthetic());
EXPECT(!TokenPosition::kLast.IsSynthetic());
EXPECT(SyntheticRoundTripTest(TokenPosition(0)));
EXPECT(SyntheticRoundTripTest(TokenPosition::kMaxSource));
EXPECT(SyntheticRoundTripTest(TokenPosition::kMinSource));
}
} // namespace dart