blob: b6ec898e50d9047a11e7089f1dc7534c81246c8e [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstdint>
#include <memory>
#include <vector>
#include "src/developer/shell/common/ast_builder.h"
#include "src/developer/shell/interpreter/test/interpreter_test.h"
shell::console::AstBuilder::NodeId NullNode;
TEST_F(InterpreterTest, ContextNotCreated) {
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(1));
Finish(kError);
ASSERT_EQ("Execution context 1 not defined.\n", GlobalErrors());
}
TEST_F(InterpreterTest, ContextCreatedTwice) {
ASSERT_CALL_OK(shell()->CreateExecutionContext(1));
ASSERT_CALL_OK(shell()->CreateExecutionContext(1));
Finish(kError);
ASSERT_EQ("Execution context 1 is already in use.\n", GlobalErrors());
}
TEST_F(InterpreterTest, NoPendingInstruction) {
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(context->id));
Finish(kExecute);
ASSERT_EQ(fuchsia_shell::wire::ExecuteResult::kAnalysisError, context->result);
std::string error_result = context->error_stream.str();
ASSERT_EQ("No pending instruction to execute.\n", error_result);
}
TEST_F(InterpreterTest, GlobalExpression) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
builder.SetRoot(builder.AddIntegerLiteral(1));
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(context->id));
Finish(kExecute);
ASSERT_EQ(fuchsia_shell::wire::ExecuteResult::kAnalysisError, context->result);
std::string error_result = context->error_stream.str();
ASSERT_EQ("Node 1:1 can't be a root node.\n", error_result);
}
TEST_F(InterpreterTest, BadAst) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
builder.AddIntegerLiteral(1, true);
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(context->id));
Finish(kExecute);
ASSERT_EQ(fuchsia_shell::wire::ExecuteResult::kAnalysisError, context->result);
std::string error_result = context->error_stream.str();
ASSERT_EQ("Pending AST nodes for execution context 1.\n", error_result);
}
TEST_F(InterpreterTest, VariableDefinition) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
builder.AddVariableDeclaration("foo", builder.TypeUint64(), NullNode, false, true);
builder.AddVariableDeclaration("bar", builder.TypeInt64(), builder.AddIntegerLiteral(1, true),
true, true);
builder.AddVariableDeclaration("x", builder.TypeUint64(), builder.AddIntegerLiteral(10, false),
true, true);
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->DumpExecutionContext(context->id));
Finish(kDump);
ASSERT_FALSE(last_text_result_partial());
ASSERT_EQ(text_results().size(), static_cast<size_t>(3));
ASSERT_EQ(text_results()[0], "var foo: uint64\n");
ASSERT_EQ(text_results()[1], "const bar: int64(-1)\n");
ASSERT_EQ(text_results()[2], "const x: uint64(10)\n");
}
TEST_F(InterpreterTest, BuiltinTypes) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
builder.AddVariableDeclaration("b", builder.TypeBool(), NullNode, false, true);
builder.AddVariableDeclaration("c", builder.TypeChar(), NullNode, false, true);
builder.AddVariableDeclaration("s", builder.TypeString(), NullNode, false, true);
builder.AddVariableDeclaration("i8", builder.TypeInt8(), NullNode, false, true);
builder.AddVariableDeclaration("u8", builder.TypeUint8(), NullNode, false, true);
builder.AddVariableDeclaration("i16", builder.TypeInt16(), NullNode, false, true);
builder.AddVariableDeclaration("u16", builder.TypeUint16(), NullNode, false, true);
builder.AddVariableDeclaration("i32", builder.TypeInt32(), NullNode, false, true);
builder.AddVariableDeclaration("u32", builder.TypeUint32(), NullNode, false, true);
builder.AddVariableDeclaration("i64", builder.TypeInt64(), NullNode, false, true);
builder.AddVariableDeclaration("u64", builder.TypeUint64(), NullNode, false, true);
builder.AddVariableDeclaration("big_int", builder.TypeInteger(), NullNode, false, true);
builder.AddVariableDeclaration("f32", builder.TypeFloat32(), NullNode, false, true);
builder.AddVariableDeclaration("f64", builder.TypeFloat64(), NullNode, false, true);
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->DumpExecutionContext(context->id));
Finish(kDump);
ASSERT_FALSE(last_text_result_partial());
ASSERT_EQ(text_results().size(), static_cast<size_t>(14));
ASSERT_EQ(text_results()[0], "var b: bool\n");
ASSERT_EQ(text_results()[1], "var c: char\n");
ASSERT_EQ(text_results()[2], "var s: string\n");
ASSERT_EQ(text_results()[3], "var i8: int8\n");
ASSERT_EQ(text_results()[4], "var u8: uint8\n");
ASSERT_EQ(text_results()[5], "var i16: int16\n");
ASSERT_EQ(text_results()[6], "var u16: uint16\n");
ASSERT_EQ(text_results()[7], "var i32: int32\n");
ASSERT_EQ(text_results()[8], "var u32: uint32\n");
ASSERT_EQ(text_results()[9], "var i64: int64\n");
ASSERT_EQ(text_results()[10], "var u64: uint64\n");
ASSERT_EQ(text_results()[11], "var big_int: integer\n");
ASSERT_EQ(text_results()[12], "var f32: float32\n");
ASSERT_EQ(text_results()[13], "var f64: float64\n");
}
TEST_F(InterpreterTest, Objects) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
{
std::vector<std::string> names;
std::vector<shell::console::AstBuilder::NodeId> values;
std::vector<fuchsia_shell::wire::ShellType> types;
shell::console::AstBuilder::NodePair object_pair =
AddObject(builder, names, values, std::move(types));
builder.AddVariableDeclaration("obj1", builder.TypeObject(object_pair.schema_node),
object_pair.value_node, /*is_const=*/false, /*is_root=*/true);
}
{
std::vector<std::string> names{"alpha", "beta"};
std::vector<shell::console::AstBuilder::NodeId> values{builder.AddIntegerLiteral(4, false),
builder.AddIntegerLiteral(5, false)};
std::vector<fuchsia_shell::wire::ShellType> types;
types.emplace_back(builder.TypeUint64());
types.emplace_back(builder.TypeUint64());
shell::console::AstBuilder::NodePair object_pair =
AddObject(builder, names, values, std::move(types));
builder.AddVariableDeclaration("obj2", builder.TypeObject(object_pair.schema_node),
object_pair.value_node, /*is_const=*/false, /*is_root=*/true);
}
{
std::vector<std::string> names{"alpha", "beta"};
std::vector<shell::console::AstBuilder::NodeId> values{builder.AddStringLiteral("Hello"),
builder.AddStringLiteral("world!")};
std::vector<fuchsia_shell::wire::ShellType> types;
types.emplace_back(builder.TypeString());
types.emplace_back(builder.TypeString());
shell::console::AstBuilder::NodePair object_pair =
AddObject(builder, names, values, std::move(types));
builder.AddVariableDeclaration("obj3", builder.TypeObject(object_pair.schema_node),
object_pair.value_node, /*is_const=*/false, /*is_root=*/true);
}
{
std::vector<std::string> inner_names{"alpha", "beta"};
std::vector<shell::console::AstBuilder::NodeId> inner_values{
builder.AddStringLiteral("Hello"), builder.AddStringLiteral("world!")};
std::vector<fuchsia_shell::wire::ShellType> inner_types;
inner_types.emplace_back(builder.TypeString());
inner_types.emplace_back(builder.TypeString());
shell::console::AstBuilder::NodePair inner_object_pair =
AddObject(builder, inner_names, inner_values, std::move(inner_types));
std::vector<std::string> outer_names{"inner", "extra"};
std::vector<shell::console::AstBuilder::NodeId> outer_values{
inner_object_pair.value_node, builder.AddStringLiteral("Extra value")};
std::vector<fuchsia_shell::wire::ShellType> outer_types;
outer_types.emplace_back(builder.TypeObject(inner_object_pair.schema_node));
outer_types.emplace_back(builder.TypeString());
shell::console::AstBuilder::NodePair outer_object_pair =
AddObject(builder, outer_names, outer_values, std::move(outer_types));
builder.AddVariableDeclaration("obj4", builder.TypeObject(outer_object_pair.schema_node),
outer_object_pair.value_node, /*is_const=*/false,
/*is_root=*/true);
}
builder.AddEmitResult(builder.AddVariable("obj1"));
builder.AddEmitResult(builder.AddVariable("obj2"));
builder.AddEmitResult(builder.AddVariable("obj3"));
builder.AddEmitResult(builder.AddVariable("obj4"));
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(context->id));
Finish(kExecute);
ASSERT_EQ(fuchsia_shell::wire::ExecuteResult::kOk, context->GetResult());
CHECK_RESULT(0, "{}");
CHECK_RESULT(1, "{alpha: uint64(4), beta: uint64(5)}");
CHECK_RESULT(2, "{alpha: string(\"Hello\"), beta: string(\"world!\")}");
CHECK_RESULT(3,
"{inner: {alpha: string(\"Hello\"), beta: string(\"world!\")},"
" extra: string(\"Extra value\")}");
}
TEST_F(InterpreterTest, VariableOk) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
builder.AddVariableDeclaration("foo", builder.TypeUint64(), builder.AddIntegerLiteral(1, false),
false, true);
builder.AddVariableDeclaration("bar", builder.TypeUint64(), builder.AddIntegerLiteral(10, false),
false, true);
builder.AddVariableDeclaration("groucho", builder.TypeString(),
builder.AddStringLiteral("A Marx brother"), false, true);
builder.AddEmitResult(builder.AddVariable("foo"));
builder.AddEmitResult(builder.AddVariable("bar"));
builder.AddEmitResult(builder.AddVariable("groucho"));
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(context->id));
Finish(kExecute);
ASSERT_EQ(fuchsia_shell::wire::ExecuteResult::kOk, context->GetResult());
CHECK_RESULT(0, "1");
CHECK_RESULT(1, "10");
CHECK_RESULT(2, "\"A Marx brother\"");
}
TEST_F(InterpreterTest, VariableNoType) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
builder.AddVariableDeclaration("bar", builder.TypeUndef(), NullNode, false, true);
builder.AddVariableDeclaration("foo", builder.TypeUndef(), builder.AddIntegerLiteral(1, false),
false, true);
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(context->id));
Finish(kExecute);
ASSERT_EQ(fuchsia_shell::wire::ExecuteResult::kAnalysisError, context->result);
std::string error_result = context->error_stream.str();
ASSERT_EQ(
"node 1:1 Type not defined.\n"
"node 1:3 Type not defined.\n",
error_result);
}
TEST_F(InterpreterTest, VariableDefinedTwice) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
builder.AddVariableDeclaration("bar", builder.TypeUint64(), NullNode, false, true);
builder.AddVariableDeclaration("bar", builder.TypeUint64(), NullNode, false, true);
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(context->id));
Finish(kExecute);
ASSERT_EQ(fuchsia_shell::wire::ExecuteResult::kAnalysisError, context->result);
std::string error_result = context->error_stream.str();
ASSERT_EQ(
"node 1:2 Variable 'bar' already defined.\n"
"node 1:1 First definition.\n",
error_result);
}
TEST_F(InterpreterTest, BadIntegerLiterals) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
builder.AddVariableDeclaration("i8p", builder.TypeInt8(), builder.AddIntegerLiteral(0x80, false),
true, true);
builder.AddVariableDeclaration("i8n", builder.TypeInt8(), builder.AddIntegerLiteral(0x81, true),
true, true);
builder.AddVariableDeclaration("u8p", builder.TypeUint8(),
builder.AddIntegerLiteral(0x100, false), true, true);
builder.AddVariableDeclaration("u8n", builder.TypeUint8(), builder.AddIntegerLiteral(1, true),
true, true);
builder.AddVariableDeclaration("i16p", builder.TypeInt16(),
builder.AddIntegerLiteral(0x8000, false), true, true);
builder.AddVariableDeclaration("i16n", builder.TypeInt16(),
builder.AddIntegerLiteral(0x8001, true), true, true);
builder.AddVariableDeclaration("u16p", builder.TypeUint16(),
builder.AddIntegerLiteral(0x10000, false), true, true);
builder.AddVariableDeclaration("u16n", builder.TypeUint16(), builder.AddIntegerLiteral(1, true),
true, true);
builder.AddVariableDeclaration("i32p", builder.TypeInt32(),
builder.AddIntegerLiteral(0x80000000, false), true, true);
builder.AddVariableDeclaration("i32n", builder.TypeInt32(),
builder.AddIntegerLiteral(0x80000001, true), true, true);
builder.AddVariableDeclaration("u32p", builder.TypeUint32(),
builder.AddIntegerLiteral(0x100000000L, false), true, true);
builder.AddVariableDeclaration("u32n", builder.TypeUint32(), builder.AddIntegerLiteral(1, true),
true, true);
builder.AddVariableDeclaration("i64p", builder.TypeInt64(),
builder.AddIntegerLiteral(0x8000000000000000UL, false), true,
true);
builder.AddVariableDeclaration("i64n", builder.TypeInt64(),
builder.AddIntegerLiteral(0x8000000000000001UL, true), true, true);
builder.AddVariableDeclaration("u64n", builder.TypeUint64(), builder.AddIntegerLiteral(1, true),
true, true);
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(context->id));
Finish(kExecute);
ASSERT_EQ(fuchsia_shell::wire::ExecuteResult::kAnalysisError, context->result);
std::string error_result = context->error_stream.str();
ASSERT_EQ(
"node 1:1 Can't create an integer literal of type int8 with 128.\n"
"node 1:3 Can't create an integer literal of type int8 with -129.\n"
"node 1:5 Can't create an integer literal of type uint8 with 256.\n"
"node 1:7 Can't create an integer literal of type uint8 with -1.\n"
"node 1:9 Can't create an integer literal of type int16 with 32768.\n"
"node 1:11 Can't create an integer literal of type int16 with -32769.\n"
"node 1:13 Can't create an integer literal of type uint16 with 65536.\n"
"node 1:15 Can't create an integer literal of type uint16 with -1.\n"
"node 1:17 Can't create an integer literal of type int32 with 2147483648.\n"
"node 1:19 Can't create an integer literal of type int32 with -2147483649.\n"
"node 1:21 Can't create an integer literal of type uint32 with 4294967296.\n"
"node 1:23 Can't create an integer literal of type uint32 with -1.\n"
"node 1:25 Can't create an integer literal of type int64 with 9223372036854775808.\n"
"node 1:27 Can't create an integer literal of type int64 with -9223372036854775809.\n"
"node 1:29 Can't create an integer literal of type uint64 with -1.\n",
error_result);
}
TEST_F(InterpreterTest, GoodIntegerLiterals) {
constexpr uint64_t kFileId = 1;
InterpreterTestContext* context = CreateContext();
ASSERT_CALL_OK(shell()->CreateExecutionContext(context->id));
shell::console::AstBuilder builder(kFileId);
builder.AddVariableDeclaration("i8p", builder.TypeInt8(), builder.AddIntegerLiteral(0x7f, false),
true, true);
builder.AddVariableDeclaration("i8n", builder.TypeInt8(), builder.AddIntegerLiteral(0x80, true),
true, true);
builder.AddVariableDeclaration("u8p", builder.TypeUint8(), builder.AddIntegerLiteral(0xff, false),
true, true);
builder.AddVariableDeclaration("u8n", builder.TypeUint8(), builder.AddIntegerLiteral(0, false),
true, true);
builder.AddVariableDeclaration("i16p", builder.TypeInt16(),
builder.AddIntegerLiteral(0x7fff, false), true, true);
builder.AddVariableDeclaration("i16n", builder.TypeInt16(),
builder.AddIntegerLiteral(0x8000, true), true, true);
builder.AddVariableDeclaration("u16p", builder.TypeUint16(),
builder.AddIntegerLiteral(0xffff, false), true, true);
builder.AddVariableDeclaration("u16n", builder.TypeUint16(), builder.AddIntegerLiteral(0, false),
true, true);
builder.AddVariableDeclaration("i32p", builder.TypeInt32(),
builder.AddIntegerLiteral(0x7fffffff, false), true, true);
builder.AddVariableDeclaration("i32n", builder.TypeInt32(),
builder.AddIntegerLiteral(0x80000000, true), true, true);
builder.AddVariableDeclaration("u32p", builder.TypeUint32(),
builder.AddIntegerLiteral(0xffffffffL, false), true, true);
builder.AddVariableDeclaration("u32n", builder.TypeUint32(), builder.AddIntegerLiteral(0, false),
true, true);
builder.AddVariableDeclaration("i64p", builder.TypeInt64(),
builder.AddIntegerLiteral(0x7fffffffffffffffUL, false), true,
true);
builder.AddVariableDeclaration("i64n", builder.TypeInt64(),
builder.AddIntegerLiteral(0x8000000000000000UL, true), true, true);
builder.AddVariableDeclaration("u64p", builder.TypeUint64(),
builder.AddIntegerLiteral(0xffffffffffffffffUL, false), true,
true);
builder.AddVariableDeclaration("u64n", builder.TypeUint64(), builder.AddIntegerLiteral(0, false),
true, true);
ASSERT_CALL_OK(shell()->AddNodes(context->id, builder.DefsAsVectorView()));
ASSERT_CALL_OK(shell()->ExecuteExecutionContext(context->id));
Finish(kExecute);
ASSERT_EQ(fuchsia_shell::wire::ExecuteResult::kOk, context->GetResult());
}