huangyu c658ccf319 Update runtime_core code
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
2022-07-17 10:20:32 +08:00

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