blob: 9de7a0cb620728848db9936fd66d0d2e52700075 [file] [log] [blame]
// 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();
TokenPosition token_pos = instr->token_pos();
if (token_pos.IsSynthetic()) {
token_pos = token_pos.FromSynthetic();
}
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;
}
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