mirror of
https://gitee.com/openharmony/arkcompiler_runtime_core
synced 2025-04-15 17:10:27 +00:00

Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/I5G96F Test: Test262 suit, ark unittest, rk3568 XTS, ark previewer demo Signed-off-by: huangyu <huangyu76@huawei.com> Change-Id: I3f63d129a07deaa27a390f556dcaa5651c098185
811 lines
27 KiB
C++
811 lines
27 KiB
C++
/**
|
|
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <iomanip>
|
|
#include <tuple>
|
|
#include <vector>
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
#include "annotation_data_accessor.h"
|
|
#include "assembly-emitter.h"
|
|
#include "assembly-parser.h"
|
|
#include "class_data_accessor-inl.h"
|
|
#include "code_data_accessor-inl.h"
|
|
#include "debug_data_accessor-inl.h"
|
|
#include "field_data_accessor-inl.h"
|
|
#include "file_items.h"
|
|
#include "lexer.h"
|
|
#include "method_data_accessor-inl.h"
|
|
#include "param_annotations_data_accessor.h"
|
|
#include "proto_data_accessor-inl.h"
|
|
#include "utils/span.h"
|
|
#include "utils/leb128.h"
|
|
#include "utils/utf.h"
|
|
#include "bytecode_instruction-inl.h"
|
|
|
|
namespace panda::test {
|
|
|
|
using namespace panda::pandasm;
|
|
|
|
static const uint8_t *GetTypeDescriptor(const std::string &name, std::string *storage)
|
|
{
|
|
*storage = "L" + name + ";";
|
|
std::replace(storage->begin(), storage->end(), '.', '/');
|
|
return utf::CStringAsMutf8(storage->c_str());
|
|
}
|
|
|
|
TEST(emittertests, test)
|
|
{
|
|
Parser p;
|
|
|
|
auto source = R"( # 1
|
|
.record R { # 2
|
|
i32 sf <static> # 3
|
|
i8 if # 4
|
|
} # 5
|
|
# 6
|
|
.function void main() { # 7
|
|
return.void # 8
|
|
} # 9
|
|
)";
|
|
|
|
std::string source_filename = "source.pa";
|
|
auto res = p.Parse(source, source_filename);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
// Check _GLOBAL class
|
|
|
|
{
|
|
std::string descriptor;
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
ASSERT_FALSE(pf->IsExternal(class_id));
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
ASSERT_EQ(cda.GetSuperClassId().GetOffset(), 0U);
|
|
ASSERT_EQ(cda.GetAccessFlags(), ACC_PUBLIC);
|
|
ASSERT_EQ(cda.GetFieldsNumber(), 0U);
|
|
ASSERT_EQ(cda.GetMethodsNumber(), 1U);
|
|
ASSERT_EQ(cda.GetIfacesNumber(), 0U);
|
|
|
|
ASSERT_FALSE(cda.GetSourceFileId().has_value());
|
|
|
|
cda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
|
|
|
|
cda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
|
|
|
|
cda.EnumerateFields([](panda_file::FieldDataAccessor &) { ASSERT_TRUE(false); });
|
|
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
|
|
ASSERT_FALSE(mda.IsExternal());
|
|
ASSERT_EQ(mda.GetClassId(), class_id);
|
|
ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(mda.GetNameId()).data, utf::CStringAsMutf8("main")),
|
|
0);
|
|
|
|
panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
|
|
ASSERT_EQ(pda.GetNumArgs(), 0U);
|
|
ASSERT_EQ(pda.GetReturnType().GetId(), panda_file::Type::TypeId::VOID);
|
|
|
|
ASSERT_EQ(mda.GetAccessFlags(), ACC_STATIC);
|
|
ASSERT_TRUE(mda.GetCodeId().has_value());
|
|
|
|
panda_file::CodeDataAccessor cdacc(*pf, mda.GetCodeId().value());
|
|
ASSERT_EQ(cdacc.GetNumVregs(), 0U);
|
|
ASSERT_EQ(cdacc.GetNumArgs(), 0U);
|
|
ASSERT_EQ(cdacc.GetCodeSize(), 1U);
|
|
ASSERT_EQ(cdacc.GetTriesSize(), 0U);
|
|
|
|
ASSERT_FALSE(mda.GetRuntimeParamAnnotationId().has_value());
|
|
ASSERT_FALSE(mda.GetParamAnnotationId().has_value());
|
|
ASSERT_TRUE(mda.GetDebugInfoId().has_value());
|
|
|
|
panda_file::DebugInfoDataAccessor dda(*pf, mda.GetDebugInfoId().value());
|
|
ASSERT_EQ(dda.GetLineStart(), 8U);
|
|
ASSERT_EQ(dda.GetNumParams(), 0U);
|
|
|
|
mda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
|
|
mda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
|
|
});
|
|
}
|
|
|
|
// Check R class
|
|
|
|
{
|
|
std::string descriptor;
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
ASSERT_FALSE(pf->IsExternal(class_id));
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
ASSERT_EQ(cda.GetSuperClassId().GetOffset(), 0U);
|
|
ASSERT_EQ(cda.GetAccessFlags(), 0);
|
|
ASSERT_EQ(cda.GetFieldsNumber(), 2U);
|
|
ASSERT_EQ(cda.GetMethodsNumber(), 0U);
|
|
ASSERT_EQ(cda.GetIfacesNumber(), 0U);
|
|
|
|
// We emit SET_FILE in debuginfo
|
|
ASSERT_TRUE(cda.GetSourceFileId().has_value());
|
|
|
|
EXPECT_EQ(std::string(reinterpret_cast<const char *>(pf->GetStringData(cda.GetSourceFileId().value()).data)),
|
|
source_filename);
|
|
|
|
cda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
|
|
|
|
cda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
|
|
|
|
struct FieldData {
|
|
std::string name;
|
|
panda_file::Type::TypeId type_id;
|
|
uint32_t access_flags;
|
|
};
|
|
|
|
std::vector<FieldData> fields {{"sf", panda_file::Type::TypeId::I32, ACC_STATIC},
|
|
{"if", panda_file::Type::TypeId::I8, 0}};
|
|
|
|
size_t i = 0;
|
|
cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
|
|
ASSERT_FALSE(fda.IsExternal());
|
|
ASSERT_EQ(fda.GetClassId(), class_id);
|
|
ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(fda.GetNameId()).data,
|
|
utf::CStringAsMutf8(fields[i].name.c_str())),
|
|
0);
|
|
|
|
ASSERT_EQ(fda.GetType(), panda_file::Type(fields[i].type_id).GetFieldEncoding());
|
|
ASSERT_EQ(fda.GetAccessFlags(), fields[i].access_flags);
|
|
|
|
fda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
|
|
fda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
|
|
|
|
++i;
|
|
});
|
|
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &) { ASSERT_TRUE(false); });
|
|
}
|
|
}
|
|
|
|
uint8_t GetSpecialOpcode(uint32_t pc_inc, int32_t line_inc)
|
|
{
|
|
return (line_inc - panda_file::LineNumberProgramItem::LINE_BASE) +
|
|
(pc_inc * panda_file::LineNumberProgramItem::LINE_RANGE) + panda_file::LineNumberProgramItem::OPCODE_BASE;
|
|
}
|
|
|
|
TEST(emittertests, debuginfo)
|
|
{
|
|
Parser p;
|
|
|
|
auto source = R"(
|
|
.function void main() {
|
|
ldai.64 0 # line 3, pc 0
|
|
# line 4
|
|
# line 5
|
|
# line 6
|
|
# line 7
|
|
# line 8
|
|
# line 9
|
|
# line 10
|
|
# line 11
|
|
# line 12
|
|
# line 13
|
|
# line 14
|
|
ldai.64 1 # line 15, pc 9
|
|
return.void # line 16, pc 18
|
|
}
|
|
)";
|
|
|
|
std::string source_filename = "source.pa";
|
|
auto res = p.Parse(source, source_filename);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
std::string descriptor;
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
|
|
panda_file::CodeDataAccessor cdacc(*pf, mda.GetCodeId().value());
|
|
ASSERT_TRUE(mda.GetDebugInfoId().has_value());
|
|
|
|
panda_file::DebugInfoDataAccessor dda(*pf, mda.GetDebugInfoId().value());
|
|
ASSERT_EQ(dda.GetLineStart(), 3U);
|
|
ASSERT_EQ(dda.GetNumParams(), 0U);
|
|
|
|
const uint8_t *program = dda.GetLineNumberProgram();
|
|
Span<const uint8_t> constant_pool = dda.GetConstantPool();
|
|
|
|
std::vector<uint8_t> opcodes {static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::SET_FILE),
|
|
static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::ADVANCE_PC),
|
|
static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::ADVANCE_LINE),
|
|
GetSpecialOpcode(0, 0),
|
|
GetSpecialOpcode(9, 1),
|
|
static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::END_SEQUENCE)};
|
|
|
|
EXPECT_THAT(opcodes, ::testing::ElementsAreArray(program, opcodes.size()));
|
|
|
|
size_t size {};
|
|
bool is_full {};
|
|
size_t constant_pool_offset = 0;
|
|
|
|
uint32_t offset {};
|
|
|
|
std::tie(offset, size, is_full) = leb128::DecodeUnsigned<uint32_t>(&constant_pool[constant_pool_offset]);
|
|
constant_pool_offset += size;
|
|
ASSERT_TRUE(is_full);
|
|
EXPECT_EQ(
|
|
std::string(reinterpret_cast<const char *>(pf->GetStringData(panda_file::File::EntityId(offset)).data)),
|
|
source_filename);
|
|
|
|
uint32_t pc_inc;
|
|
std::tie(pc_inc, size, is_full) = leb128::DecodeUnsigned<uint32_t>(&constant_pool[constant_pool_offset]);
|
|
constant_pool_offset += size;
|
|
ASSERT_TRUE(is_full);
|
|
EXPECT_EQ(pc_inc, 9U);
|
|
|
|
int32_t line_inc;
|
|
std::tie(line_inc, size, is_full) = leb128::DecodeSigned<int32_t>(&constant_pool[constant_pool_offset]);
|
|
constant_pool_offset += size;
|
|
ASSERT_TRUE(is_full);
|
|
EXPECT_EQ(line_inc, 12);
|
|
|
|
EXPECT_EQ(constant_pool_offset, constant_pool.size());
|
|
});
|
|
}
|
|
|
|
TEST(emittertests, exceptions)
|
|
{
|
|
Parser p;
|
|
|
|
auto source = R"(
|
|
.record Exception1 {}
|
|
.record Exception2 {}
|
|
|
|
.function void main() {
|
|
ldai.64 0
|
|
try_begin:
|
|
ldai.64 1
|
|
ldai.64 2
|
|
try_end:
|
|
ldai.64 3
|
|
catch_begin1:
|
|
ldai.64 4
|
|
catch_begin2:
|
|
ldai.64 5
|
|
catchall_begin:
|
|
ldai.64 6
|
|
|
|
.catch Exception1, try_begin, try_end, catch_begin1
|
|
.catch Exception2, try_begin, try_end, catch_begin2
|
|
.catchall try_begin, try_end, catchall_begin
|
|
}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
std::string descriptor;
|
|
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
|
|
panda_file::CodeDataAccessor cdacc(*pf, mda.GetCodeId().value());
|
|
ASSERT_EQ(cdacc.GetNumVregs(), 0U);
|
|
ASSERT_EQ(cdacc.GetNumArgs(), 0U);
|
|
ASSERT_EQ(cdacc.GetTriesSize(), 1);
|
|
|
|
cdacc.EnumerateTryBlocks([&](panda_file::CodeDataAccessor::TryBlock &try_block) {
|
|
EXPECT_EQ(try_block.GetStartPc(), 9);
|
|
EXPECT_EQ(try_block.GetLength(), 18);
|
|
EXPECT_EQ(try_block.GetNumCatches(), 3);
|
|
|
|
struct CatchInfo {
|
|
panda_file::File::EntityId type_id;
|
|
uint32_t handler_pc;
|
|
};
|
|
|
|
std::vector<CatchInfo> catch_infos {{pf->GetClassId(GetTypeDescriptor("Exception1", &descriptor)), 4 * 9},
|
|
{pf->GetClassId(GetTypeDescriptor("Exception2", &descriptor)), 5 * 9},
|
|
{panda_file::File::EntityId(), 6 * 9}};
|
|
|
|
size_t i = 0;
|
|
try_block.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catch_block) {
|
|
auto idx = catch_block.GetTypeIdx();
|
|
auto id = idx != panda_file::INVALID_INDEX ? pf->ResolveClassIndex(mda.GetMethodId(), idx)
|
|
: panda_file::File::EntityId();
|
|
EXPECT_EQ(id, catch_infos[i].type_id);
|
|
EXPECT_EQ(catch_block.GetHandlerPc(), catch_infos[i].handler_pc);
|
|
++i;
|
|
|
|
return true;
|
|
});
|
|
|
|
return true;
|
|
});
|
|
});
|
|
}
|
|
|
|
TEST(emittertests, errors)
|
|
{
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.record A {
|
|
B b
|
|
}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_EQ(pf, nullptr);
|
|
ASSERT_EQ(AsmEmitter::GetLastError(), "Field A.b has undefined type");
|
|
}
|
|
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.function void A.b() {}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_EQ(pf, nullptr);
|
|
ASSERT_EQ(AsmEmitter::GetLastError(), "Function A.b is bound to undefined record A");
|
|
}
|
|
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.function A b() {}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_EQ(pf, nullptr);
|
|
ASSERT_EQ(AsmEmitter::GetLastError(), "Function b has undefined return type");
|
|
}
|
|
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.function void a(b a0) {}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_EQ(pf, nullptr);
|
|
ASSERT_EQ(AsmEmitter::GetLastError(), "Argument 0 of function a has undefined type");
|
|
}
|
|
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.record A <external>
|
|
.function void A.x() {}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_EQ(pf, nullptr);
|
|
ASSERT_EQ(AsmEmitter::GetLastError(), "Non-external function A.x is bound to external record");
|
|
}
|
|
}
|
|
|
|
TEST(emittertests, language)
|
|
{
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.function void foo() {}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
std::string descriptor;
|
|
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
|
|
ASSERT_FALSE(cda.GetSourceLang());
|
|
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) { ASSERT_FALSE(mda.GetSourceLang()); });
|
|
}
|
|
}
|
|
|
|
TEST(emittertests, constructors)
|
|
{
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.record R {}
|
|
.function void R.foo(R a0) <ctor> {}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
std::string descriptor;
|
|
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
|
|
auto *name = utf::Mutf8AsCString(pf->GetStringData(mda.GetNameId()).data);
|
|
ASSERT_STREQ(name, ".ctor");
|
|
});
|
|
}
|
|
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.record R {}
|
|
.function void R.foo(R a0) <cctor> {}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
std::string descriptor;
|
|
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
|
|
auto *name = utf::Mutf8AsCString(pf->GetStringData(mda.GetNameId()).data);
|
|
ASSERT_STREQ(name, ".cctor");
|
|
});
|
|
}
|
|
}
|
|
|
|
TEST(emittertests, field_value)
|
|
{
|
|
Parser p;
|
|
|
|
auto source = R"(
|
|
.record panda.String <external>
|
|
|
|
.record R {
|
|
u1 f_u1 <value=1>
|
|
i8 f_i8 <value=2>
|
|
u8 f_u8 <value=128>
|
|
i16 f_i16 <value=256>
|
|
u16 f_u16 <value=32768>
|
|
i32 f_i32 <value=65536>
|
|
u32 f_u32 <value=2147483648>
|
|
i64 f_i64 <value=4294967296>
|
|
u64 f_u64 <value=9223372036854775808>
|
|
f32 f_f32 <value=1.0>
|
|
f64 f_f64 <value=2.0>
|
|
panda.String f_str <value="str">
|
|
}
|
|
)";
|
|
|
|
struct FieldData {
|
|
std::string name;
|
|
panda_file::Type::TypeId type_id;
|
|
std::variant<int32_t, uint32_t, int64_t, uint64_t, float, double, std::string> value;
|
|
};
|
|
|
|
std::vector<FieldData> data {
|
|
{"f_u1", panda_file::Type::TypeId::U1, static_cast<uint32_t>(1)},
|
|
{"f_i8", panda_file::Type::TypeId::I8, static_cast<int32_t>(2)},
|
|
{"f_u8", panda_file::Type::TypeId::U8, static_cast<uint32_t>(128)},
|
|
{"f_i16", panda_file::Type::TypeId::I16, static_cast<int32_t>(256)},
|
|
{"f_u16", panda_file::Type::TypeId::U16, static_cast<uint32_t>(32768)},
|
|
{"f_i32", panda_file::Type::TypeId::I32, static_cast<int32_t>(65536)},
|
|
{"f_u32", panda_file::Type::TypeId::U32, static_cast<uint32_t>(2147483648)},
|
|
{"f_i64", panda_file::Type::TypeId::I64, static_cast<int64_t>(4294967296)},
|
|
{"f_u64", panda_file::Type::TypeId::U64, static_cast<uint64_t>(9223372036854775808ULL)},
|
|
{"f_f32", panda_file::Type::TypeId::F32, static_cast<float>(1.0)},
|
|
{"f_f64", panda_file::Type::TypeId::F64, static_cast<double>(2.0)},
|
|
{"f_str", panda_file::Type::TypeId::REFERENCE, "str"}};
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
std::string descriptor;
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
ASSERT_FALSE(pf->IsExternal(class_id));
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
ASSERT_EQ(cda.GetFieldsNumber(), data.size());
|
|
|
|
auto panda_string_id = pf->GetClassId(GetTypeDescriptor("panda.String", &descriptor));
|
|
|
|
size_t idx = 0;
|
|
cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
|
|
const FieldData &field_data = data[idx];
|
|
|
|
ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(fda.GetNameId()).data,
|
|
utf::CStringAsMutf8(field_data.name.c_str())),
|
|
0);
|
|
|
|
panda_file::Type type(field_data.type_id);
|
|
uint32_t type_value;
|
|
if (type.IsReference()) {
|
|
type_value = panda_string_id.GetOffset();
|
|
} else {
|
|
type_value = type.GetFieldEncoding();
|
|
}
|
|
|
|
ASSERT_EQ(fda.GetType(), type_value);
|
|
|
|
switch (field_data.type_id) {
|
|
case panda_file::Type::TypeId::U1: {
|
|
auto result = fda.GetValue<uint8_t>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<uint32_t>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::I8: {
|
|
auto result = fda.GetValue<int8_t>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<int32_t>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::U8: {
|
|
auto result = fda.GetValue<uint8_t>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<uint32_t>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::I16: {
|
|
auto result = fda.GetValue<int16_t>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<int32_t>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::U16: {
|
|
auto result = fda.GetValue<uint16_t>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<uint32_t>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::I32: {
|
|
auto result = fda.GetValue<int32_t>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<int32_t>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::U32: {
|
|
auto result = fda.GetValue<uint32_t>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<uint32_t>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::I64: {
|
|
auto result = fda.GetValue<int64_t>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<int64_t>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::U64: {
|
|
auto result = fda.GetValue<uint64_t>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<uint64_t>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::F32: {
|
|
auto result = fda.GetValue<float>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<float>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::F64: {
|
|
auto result = fda.GetValue<double>();
|
|
ASSERT_TRUE(result);
|
|
ASSERT_EQ(result.value(), std::get<double>(field_data.value));
|
|
break;
|
|
}
|
|
case panda_file::Type::TypeId::REFERENCE: {
|
|
auto result = fda.GetValue<uint32_t>();
|
|
ASSERT_TRUE(result);
|
|
|
|
panda_file::File::EntityId string_id(result.value());
|
|
auto val = std::get<std::string>(field_data.value);
|
|
|
|
ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(string_id).data, utf::CStringAsMutf8(val.c_str())),
|
|
0);
|
|
break;
|
|
}
|
|
default: {
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
++idx;
|
|
});
|
|
}
|
|
|
|
TEST(emittertests, tagged_in_func_decl)
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.function any foo(any a0) <noimpl>
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
std::string descriptor;
|
|
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
|
|
size_t num_methods = 0;
|
|
const auto tagged = panda_file::Type(panda_file::Type::TypeId::TAGGED);
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
|
|
panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
|
|
ASSERT_EQ(tagged, pda.GetReturnType());
|
|
ASSERT_EQ(1, pda.GetNumArgs());
|
|
ASSERT_EQ(tagged, pda.GetArgType(0));
|
|
|
|
++num_methods;
|
|
});
|
|
ASSERT_EQ(1, num_methods);
|
|
}
|
|
|
|
TEST(emittertests, tagged_in_field_decl)
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.record Test {
|
|
any foo
|
|
}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
std::string descriptor;
|
|
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("Test", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
|
|
size_t num_fields = 0;
|
|
const auto tagged = panda_file::Type(panda_file::Type::TypeId::TAGGED);
|
|
cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
|
|
uint32_t type = fda.GetType();
|
|
ASSERT_EQ(tagged.GetFieldEncoding(), type);
|
|
|
|
++num_fields;
|
|
});
|
|
ASSERT_EQ(1, num_fields);
|
|
}
|
|
|
|
TEST(emittertests, function_overloading_1)
|
|
{
|
|
Parser p;
|
|
auto source = R"(
|
|
.function void foo(i8 a0) {}
|
|
.function u1 foo(u1 a0) {}
|
|
|
|
.function i8 f(i32 a0) {
|
|
call foo:(i8), v0
|
|
call foo:(u1), v1
|
|
}
|
|
)";
|
|
|
|
auto res = p.Parse(source);
|
|
ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
|
|
|
|
auto pf = AsmEmitter::Emit(res.Value());
|
|
ASSERT_NE(pf, nullptr);
|
|
|
|
std::string descriptor {};
|
|
|
|
auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
|
|
ASSERT_TRUE(class_id.IsValid());
|
|
|
|
panda_file::ClassDataAccessor cda(*pf, class_id);
|
|
|
|
size_t num_methods = 0;
|
|
|
|
std::unordered_map<panda_file::File::EntityId, panda_file::Type> id_to_arg_type {};
|
|
panda_file::File::EntityId id_f {};
|
|
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
|
|
++num_methods;
|
|
|
|
panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
|
|
|
|
if (pda.GetArgType(0) == panda_file::Type(panda_file::Type::TypeId::I32)) {
|
|
id_f = mda.GetMethodId();
|
|
|
|
return;
|
|
}
|
|
|
|
id_to_arg_type.emplace(mda.GetMethodId(), pda.GetArgType(0));
|
|
});
|
|
|
|
panda_file::MethodDataAccessor mda_f(*pf, id_f);
|
|
panda_file::CodeDataAccessor cda_f(*pf, mda_f.GetCodeId().value());
|
|
|
|
const auto ins_sz = cda_f.GetCodeSize();
|
|
const auto ins_arr = cda_f.GetInstructions();
|
|
|
|
auto bc_ins = BytecodeInstruction(ins_arr);
|
|
const auto bc_ins_last = bc_ins.JumpTo(ins_sz);
|
|
|
|
while (bc_ins.GetAddress() != bc_ins_last.GetAddress()) {
|
|
const auto arg_method_idx = bc_ins.GetId().AsIndex();
|
|
const auto arg_method_id = pf->ResolveMethodIndex(id_f, arg_method_idx);
|
|
|
|
panda_file::MethodDataAccessor method_accessor(*pf, arg_method_id);
|
|
panda_file::ProtoDataAccessor proto_accessor(*pf, method_accessor.GetProtoId());
|
|
|
|
ASSERT_EQ(proto_accessor.GetArgType(0), id_to_arg_type.at(arg_method_id));
|
|
ASSERT_EQ(std::string(utf::Mutf8AsCString(pf->GetStringData(method_accessor.GetNameId()).data)), "foo");
|
|
|
|
bc_ins = bc_ins.GetNext();
|
|
}
|
|
|
|
ASSERT_EQ(3, num_methods);
|
|
}
|
|
|
|
} // namespace panda::test
|