/** * 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 #include #include #include #include #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 # 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(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 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 constant_pool = dda.GetConstantPool(); std::vector opcodes {static_cast(panda_file::LineNumberProgramItem::Opcode::SET_FILE), static_cast(panda_file::LineNumberProgramItem::Opcode::ADVANCE_PC), static_cast(panda_file::LineNumberProgramItem::Opcode::ADVANCE_LINE), GetSpecialOpcode(0, 0), GetSpecialOpcode(9, 1), static_cast(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(&constant_pool[constant_pool_offset]); constant_pool_offset += size; ASSERT_TRUE(is_full); EXPECT_EQ( std::string(reinterpret_cast(pf->GetStringData(panda_file::File::EntityId(offset)).data)), source_filename); uint32_t pc_inc; std::tie(pc_inc, size, is_full) = leb128::DecodeUnsigned(&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(&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 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 .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) {} )"; 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) {} )"; 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 .record R { u1 f_u1 i8 f_i8 u8 f_u8 i16 f_i16 u16 f_u16 i32 f_i32 u32 f_u32 i64 f_i64 u64 f_u64 f32 f_f32 f64 f_f64 panda.String f_str } )"; struct FieldData { std::string name; panda_file::Type::TypeId type_id; std::variant value; }; std::vector data { {"f_u1", panda_file::Type::TypeId::U1, static_cast(1)}, {"f_i8", panda_file::Type::TypeId::I8, static_cast(2)}, {"f_u8", panda_file::Type::TypeId::U8, static_cast(128)}, {"f_i16", panda_file::Type::TypeId::I16, static_cast(256)}, {"f_u16", panda_file::Type::TypeId::U16, static_cast(32768)}, {"f_i32", panda_file::Type::TypeId::I32, static_cast(65536)}, {"f_u32", panda_file::Type::TypeId::U32, static_cast(2147483648)}, {"f_i64", panda_file::Type::TypeId::I64, static_cast(4294967296)}, {"f_u64", panda_file::Type::TypeId::U64, static_cast(9223372036854775808ULL)}, {"f_f32", panda_file::Type::TypeId::F32, static_cast(1.0)}, {"f_f64", panda_file::Type::TypeId::F64, static_cast(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(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::I8: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::U8: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::I16: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::U16: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::I32: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::U32: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::I64: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::U64: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::F32: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::F64: { auto result = fda.GetValue(); ASSERT_TRUE(result); ASSERT_EQ(result.value(), std::get(field_data.value)); break; } case panda_file::Type::TypeId::REFERENCE: { auto result = fda.GetValue(); ASSERT_TRUE(result); panda_file::File::EntityId string_id(result.value()); auto val = std::get(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) )"; 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 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