6e0f2f4e47
Made modifications to the original VM flow graph builder so the token
position for the top frame of the overflow stack is the same as the
token position of the method (before we pointed to the opening '{' or
'=>').
Change-Id: I5c878fc238898e2fea197ea80ed4e320adca9439
Reviewed-on: https://dart-review.googlesource.com/48448
Reviewed-by: Alexander Markov <alexmarkov@google.com>
777 lines
26 KiB
C++
777 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/frontend/flow_graph_builder.h"
|
|
#include "vm/compiler/backend/il.h"
|
|
#include "vm/compiler/jit/compiler.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/dart_entry.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,
|
|
/* not building var desc */ NULL,
|
|
/* not inlining */ 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, 1);
|
|
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, 1);
|
|
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, 1);
|
|
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, 1);
|
|
|
|
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, 1);
|
|
|
|
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, 1);
|
|
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, 1);
|
|
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, 1);
|
|
|
|
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, 1);
|
|
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, 1);
|
|
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, 1);
|
|
|
|
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
|