arkcompiler_runtime_core/runtime/tests/interpreter_test.cpp
wangyantian 3fe4238952 Fix serious codecheck warnings(generated by OpenHarmony CI on December 24, 2021)
Signed-off-by: wangyantian <wangyantian@huawei.com>
2021-12-24 14:56:13 +08:00

3896 lines
135 KiB
C++

/*
* Copyright (c) 2021 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "assembly-parser.h"
#include "libpandabase/mem/pool_manager.h"
#include "libpandabase/utils/utf.h"
#include "libpandafile/bytecode_emitter.h"
#include "libpandafile/file.h"
#include "libpandafile/file_items.h"
#include "libpandafile/value.h"
#include "macros.h"
#include "runtime/bridge/bridge.h"
#include "runtime/include/class_linker.h"
#include "runtime/include/compiler_interface.h"
#include "runtime/include/mem/allocator.h"
#include "runtime/include/method.h"
#include "runtime/include/runtime.h"
#include "runtime/include/runtime_options.h"
#include "runtime/include/value-inl.h"
#include "runtime/interpreter/frame.h"
#include "runtime/mem/gc/gc.h"
#include "runtime/mem/internal_allocator.h"
#include "runtime/core/core_class_linker_extension.h"
#include "runtime/tests/class_linker_test_extension.h"
#include "runtime/tests/interpreter/test_interpreter.h"
#include "runtime/tests/interpreter/test_runtime_interface.h"
#include "runtime/include/coretypes/dyn_objects.h"
#include "runtime/include/hclass.h"
#include "runtime/handle_base-inl.h"
#include "runtime/handle_scope-inl.h"
#include "runtime/include/coretypes/native_pointer.h"
namespace panda::interpreter {
namespace test {
using DynClass = panda::coretypes::DynClass;
using DynObject = panda::coretypes::DynObject;
class InterpreterTest : public testing::Test {
public:
InterpreterTest()
{
RuntimeOptions options;
options.SetShouldLoadBootPandaFiles(false);
options.SetShouldInitializeIntrinsics(false);
options.SetRunGcInPlace(true);
options.SetVerifyCallStack(false);
options.SetGcType("epsilon");
Runtime::Create(options);
thread_ = panda::MTManagedThread::GetCurrent();
thread_->ManagedCodeBegin();
}
~InterpreterTest()
{
thread_->ManagedCodeEnd();
Runtime::Destroy();
}
protected:
panda::MTManagedThread *thread_ {nullptr};
};
auto CreateFrame(size_t nregs, Method *method, Frame *prev)
{
auto frame_deleter = [](Frame *frame) { RuntimeInterface::FreeFrame(frame); };
std::unique_ptr<Frame, decltype(frame_deleter)> frame(RuntimeInterface::CreateFrame(nregs, method, prev),
frame_deleter);
return frame;
}
static void InitializeFrame(Frame *f)
{
ManagedThread::GetCurrent()->SetCurrentFrame(f);
for (size_t i = 0; i < f->GetSize(); i++) {
f->GetVReg(i).SetValue(static_cast<int64_t>(0));
f->GetVReg(i).SetTag(static_cast<int64_t>(0));
}
}
static std::unique_ptr<Class> CreateClass(panda_file::SourceLang lang)
{
const std::string class_name("Foo");
auto cls = std::make_unique<Class>(reinterpret_cast<const uint8_t *>(class_name.data()), lang, 0, 0,
AlignUp(sizeof(Class), OBJECT_POINTER_SIZE));
return cls;
}
static std::pair<PandaUniquePtr<Method>, std::unique_ptr<const panda_file::File>> CreateMethod(
Class *klass, uint32_t access_flags, uint32_t nargs, uint32_t nregs, uint16_t *shorty,
const std::vector<uint8_t> &bytecode)
{
// Create panda_file
panda_file::ItemContainer container;
panda_file::ClassItem *class_item = container.GetOrCreateGlobalClassItem();
class_item->SetAccessFlags(ACC_PUBLIC);
panda_file::StringItem *method_name = container.GetOrCreateStringItem("test");
panda_file::PrimitiveTypeItem *ret_type =
container.CreateItem<panda_file::PrimitiveTypeItem>(panda_file::Type::TypeId::VOID);
std::vector<panda_file::MethodParamItem> params;
panda_file::ProtoItem *proto_item = container.GetOrCreateProtoItem(ret_type, params);
panda_file::MethodItem *method_item =
class_item->AddMethod(method_name, proto_item, ACC_PUBLIC | ACC_STATIC, params);
panda_file::CodeItem *code_item = container.CreateItem<panda_file::CodeItem>(nregs, nargs, bytecode);
method_item->SetCode(code_item);
panda_file::MemoryWriter mem_writer;
container.Write(&mem_writer);
auto data = mem_writer.GetData();
auto allocator = Runtime::GetCurrent()->GetInternalAllocator();
auto buf = allocator->AllocArray<uint8_t>(data.size());
(void)memcpy_s(buf, data.size(), data.data(), data.size());
os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(buf), data.size(), [](std::byte *buffer, size_t) noexcept {
auto a = Runtime::GetCurrent()->GetInternalAllocator();
a->Free(buffer);
});
auto pf = panda_file::File::OpenFromMemory(std::move(ptr));
// Create method
auto method = MakePandaUnique<Method>(klass, pf.get(), method_item->GetFileId(), code_item->GetFileId(),
access_flags | ACC_PUBLIC | ACC_STATIC, nargs, shorty);
method->SetInterpreterEntryPoint();
return {std::move(method), std::move(pf)};
}
static std::pair<PandaUniquePtr<Method>, std::unique_ptr<const panda_file::File>> CreateMethod(
Class *klass, Frame *f, const std::vector<uint8_t> &bytecode)
{
return CreateMethod(klass, 0, 0, f->GetSize(), nullptr, bytecode);
}
static std::unique_ptr<ClassLinker> CreateClassLinker([[maybe_unused]] ManagedThread *thread)
{
std::vector<std::unique_ptr<ClassLinkerExtension>> extensions;
extensions.push_back(std::make_unique<CoreClassLinkerExtension>());
auto allocator = Runtime::GetCurrent()->GetInternalAllocator();
auto class_linker = std::make_unique<ClassLinker>(allocator, std::move(extensions));
if (!class_linker->Initialize()) {
return nullptr;
}
return class_linker;
}
static ObjectHeader *CreateException(ManagedThread *thread)
{
auto class_linker = CreateClassLinker(thread);
LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *cls = class_linker->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
ObjectHeader *exception = ObjectHeader::Create(cls);
return exception;
}
TEST_F(InterpreterTest, TestMov)
{
BytecodeEmitter emitter;
constexpr int64_t IMM4_MAX = 7;
constexpr int64_t IMM8_MAX = std::numeric_limits<int8_t>::max();
constexpr int64_t IMM16_MAX = std::numeric_limits<int16_t>::max();
constexpr int64_t IMM32_MAX = std::numeric_limits<int32_t>::max();
constexpr int64_t IMM64_MAX = std::numeric_limits<int64_t>::max();
constexpr uint16_t V4_MAX = 15;
constexpr uint16_t V8_MAX = std::numeric_limits<uint8_t>::max();
constexpr uint16_t V16_MAX = std::numeric_limits<uint16_t>::max();
ObjectHeader *obj1 = ToPointer<ObjectHeader>(0xaabbccdd);
ObjectHeader *obj2 = ToPointer<ObjectHeader>(0xaabbccdd + 0x100);
ObjectHeader *obj3 = ToPointer<ObjectHeader>(0xaabbccdd + 0x200);
emitter.Movi(0, IMM4_MAX);
emitter.Movi(1, IMM8_MAX);
emitter.Movi(2U, IMM16_MAX);
emitter.Movi(3U, IMM32_MAX);
emitter.MoviWide(4U, IMM64_MAX);
emitter.Mov(V4_MAX, V4_MAX - 1);
emitter.Mov(V8_MAX, V8_MAX - 1);
emitter.Mov(V16_MAX, V16_MAX - 1);
emitter.MovWide(V4_MAX - 2U, V4_MAX - 3U);
emitter.MovWide(V16_MAX - 2U, V16_MAX - 3U);
emitter.MovObj(V4_MAX - 4U, V4_MAX - 5U);
emitter.MovObj(V8_MAX - 4U, V8_MAX - 5U);
emitter.MovObj(V16_MAX - 4U, V16_MAX - 5U);
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(std::numeric_limits<uint16_t>::max() + 1, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetVReg(V4_MAX - 1).SetPrimitive(IMM64_MAX - 1);
f->GetVReg(V8_MAX - 1).SetPrimitive(IMM64_MAX - 2U);
f->GetVReg(V16_MAX - 1).SetPrimitive(IMM64_MAX - 3U);
f->GetVReg(V4_MAX - 3U).SetPrimitive(IMM64_MAX - 4U);
f->GetVReg(V16_MAX - 3U).SetPrimitive(IMM64_MAX - 5U);
f->GetVReg(V4_MAX - 5U).SetReference(obj1);
f->GetVReg(V8_MAX - 5U).SetReference(obj2);
f->GetVReg(V16_MAX - 5U).SetReference(obj3);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
// Check movi
EXPECT_EQ(f->GetVReg(0).GetLong(), IMM4_MAX);
EXPECT_FALSE(f->GetVReg(0).HasObject());
EXPECT_EQ(f->GetVReg(1).GetLong(), IMM8_MAX);
EXPECT_FALSE(f->GetVReg(1).HasObject());
EXPECT_EQ(f->GetVReg(2U).GetLong(), IMM16_MAX);
EXPECT_FALSE(f->GetVReg(2U).HasObject());
EXPECT_EQ(f->GetVReg(3U).GetLong(), IMM32_MAX);
EXPECT_FALSE(f->GetVReg(3U).HasObject());
EXPECT_EQ(f->GetVReg(4U).GetLong(), IMM64_MAX);
EXPECT_FALSE(f->GetVReg(4U).HasObject());
// Check mov
EXPECT_EQ(f->GetVReg(V4_MAX).Get(), static_cast<int32_t>(IMM64_MAX - 1));
EXPECT_FALSE(f->GetVReg(V4_MAX).HasObject());
EXPECT_EQ(f->GetVReg(V8_MAX).Get(), static_cast<int32_t>(IMM64_MAX - 2U));
EXPECT_FALSE(f->GetVReg(V8_MAX).HasObject());
EXPECT_EQ(f->GetVReg(V16_MAX).Get(), static_cast<int32_t>(IMM64_MAX - 3U));
EXPECT_FALSE(f->GetVReg(V16_MAX).HasObject());
// Check mov.64
EXPECT_EQ(f->GetVReg(V4_MAX - 2U).GetLong(), IMM64_MAX - 4U);
EXPECT_FALSE(f->GetVReg(V4_MAX - 2U).HasObject());
EXPECT_EQ(f->GetVReg(V16_MAX - 2U).GetLong(), IMM64_MAX - 5U);
EXPECT_FALSE(f->GetVReg(V16_MAX - 2U).HasObject());
// Check mov.obj
EXPECT_EQ(f->GetVReg(V4_MAX - 4U).GetReference(), obj1);
EXPECT_TRUE(f->GetVReg(V4_MAX - 4U).HasObject());
EXPECT_EQ(f->GetVReg(V8_MAX - 4U).GetReference(), obj2);
EXPECT_TRUE(f->GetVReg(V8_MAX - 4U).HasObject());
EXPECT_EQ(f->GetVReg(V16_MAX - 4U).GetReference(), obj3);
EXPECT_TRUE(f->GetVReg(V16_MAX - 4U).HasObject());
}
TEST_F(InterpreterTest, TestLoadStoreAccumulator)
{
BytecodeEmitter emitter;
constexpr int64_t IMM8_MAX = std::numeric_limits<int8_t>::max();
constexpr int64_t IMM16_MAX = std::numeric_limits<int16_t>::max();
constexpr int64_t IMM32_MAX = std::numeric_limits<int32_t>::max();
constexpr int64_t IMM64_MAX = std::numeric_limits<int64_t>::max();
ObjectHeader *obj = ToPointer<ObjectHeader>(0xaabbccdd);
emitter.Ldai(IMM8_MAX);
emitter.Sta(0);
emitter.Ldai(IMM16_MAX);
emitter.Sta(1);
emitter.Ldai(IMM32_MAX);
emitter.Sta(2U);
emitter.LdaiWide(IMM64_MAX);
emitter.StaWide(3);
emitter.Lda(4);
emitter.Sta(5);
emitter.LdaWide(6);
emitter.StaWide(7);
emitter.LdaObj(8);
emitter.StaObj(9);
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetVReg(4U).SetPrimitive(IMM64_MAX - 1);
f->GetVReg(6U).SetPrimitive(IMM64_MAX - 2U);
f->GetVReg(8U).SetReference(obj);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetVReg(0).Get(), static_cast<int32_t>(IMM8_MAX));
EXPECT_FALSE(f->GetVReg(0).HasObject());
EXPECT_EQ(f->GetVReg(1).Get(), static_cast<int32_t>(IMM16_MAX));
EXPECT_FALSE(f->GetVReg(1).HasObject());
EXPECT_EQ(f->GetVReg(2U).Get(), static_cast<int32_t>(IMM32_MAX));
EXPECT_FALSE(f->GetVReg(2U).HasObject());
EXPECT_EQ(f->GetVReg(3U).GetLong(), IMM64_MAX);
EXPECT_FALSE(f->GetVReg(3U).HasObject());
EXPECT_EQ(f->GetVReg(5U).Get(), static_cast<int32_t>(IMM64_MAX - 1));
EXPECT_FALSE(f->GetVReg(5U).HasObject());
EXPECT_EQ(f->GetVReg(7U).GetLong(), IMM64_MAX - 2U);
EXPECT_FALSE(f->GetVReg(7U).HasObject());
EXPECT_EQ(f->GetVReg(9U).GetReference(), obj);
EXPECT_TRUE(f->GetVReg(9U).HasObject());
}
TEST_F(InterpreterTest, TestLoadString)
{
BytecodeEmitter emitter;
emitter.LdaStr(RuntimeInterface::STRING_ID.AsFileId().GetOffset());
emitter.ReturnObj();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetAcc().GetReference(),
RuntimeInterface::ResolveString(thread_->GetVM(), *method, RuntimeInterface::STRING_ID));
EXPECT_TRUE(f->GetAcc().HasObject());
}
void TestUnimpelemented(std::function<void(BytecodeEmitter *)> emit)
{
BytecodeEmitter emitter;
emit(&emitter);
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
EXPECT_DEATH_IF_SUPPORTED(Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get()), "");
}
TEST_F(InterpreterTest, LoadType)
{
BytecodeEmitter emitter;
pandasm::Parser p;
auto source = R"(
.record R {}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *thread = ManagedThread::GetCurrent();
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(thread, object_class));
emitter.LdaType(RuntimeInterface::TYPE_ID.AsIndex());
emitter.ReturnObj();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
RuntimeInterface::SetupResolvedClass(object_class);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
EXPECT_EQ(coretypes::Class::FromRuntimeClass(object_class), f->GetAcc().GetReference());
}
void TestFcmp(double v1, double v2, int64_t value, bool is_cmpg = false)
{
std::ostringstream ss;
if (is_cmpg) {
ss << "Test fcmpg.64";
} else {
ss << "Test fcmpl.64";
}
ss << ", v1 = " << v1 << ", v2 = " << v2;
BytecodeEmitter emitter;
if (is_cmpg) {
emitter.FcmpgWide(0);
} else {
emitter.FcmplWide(0);
}
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(v1);
f->GetVReg(0).SetPrimitive(v2);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetAcc().GetLong(), value) << ss.str();
EXPECT_FALSE(f->GetAcc().HasObject()) << ss.str();
}
TEST_F(InterpreterTest, TestFcmp)
{
TestFcmp(nan(""), 1.0, 1, true);
TestFcmp(1.0, nan(""), 1, true);
TestFcmp(nan(""), nan(""), 1, true);
TestFcmp(1.0, 2.0, -1, true);
TestFcmp(1.0, 1.0, 0, true);
TestFcmp(3.0, 2.0, 1, true);
TestFcmp(nan(""), 1.0, -1);
TestFcmp(1.0, nan(""), -1);
TestFcmp(nan(""), nan(""), -1);
TestFcmp(1.0, 2.0, -1);
TestFcmp(1.0, 1.0, 0);
TestFcmp(3.0, 2.0, 1);
}
void TestConditionalJmp(const std::string &mnemonic, int64_t v1, int64_t v2, int64_t r,
std::function<void(BytecodeEmitter *, uint8_t, const Label &)> emit)
{
std::ostringstream ss;
ss << "Test " << mnemonic << " with v1 = " << v1 << ", v2 = " << v2;
{
BytecodeEmitter emitter;
Label label = emitter.CreateLabel();
emit(&emitter, 0, label);
emitter.MoviWide(1, -1);
emitter.ReturnVoid();
emitter.Bind(label);
emitter.MoviWide(1, 1);
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(v1);
f->GetVReg(0).SetPrimitive(v2);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetVReg(1).GetLong(), r) << ss.str();
}
{
BytecodeEmitter emitter;
Label label1 = emitter.CreateLabel();
Label label2 = emitter.CreateLabel();
emitter.Jmp(label1);
emitter.Bind(label2);
emitter.MoviWide(1, 1);
emitter.ReturnVoid();
emitter.Bind(label1);
emit(&emitter, 0, label2);
emitter.MoviWide(1, -1);
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(v1);
f->GetVReg(0).SetPrimitive(v2);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetVReg(1).GetLong(), r) << ss.str();
if (ManagedThread::GetCurrent()->GetLanguageContext().GetLanguage() != panda_file::SourceLang::ECMASCRIPT) {
EXPECT_EQ(method->GetHotnessCounter(), r == 1 ? 1U : 0U) << ss.str();
}
}
}
void TestConditionalJmpz(const std::string &mnemonic, int64_t v, int64_t r,
std::function<void(BytecodeEmitter *, const Label &)> emit)
{
std::ostringstream ss;
ss << "Test " << mnemonic << " with v = " << v;
{
BytecodeEmitter emitter;
Label label = emitter.CreateLabel();
emit(&emitter, label);
emitter.MoviWide(0, -1);
emitter.ReturnVoid();
emitter.Bind(label);
emitter.MoviWide(0, 1);
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(v);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetVReg(0).GetLong(), r) << ss.str();
}
{
BytecodeEmitter emitter;
Label label1 = emitter.CreateLabel();
Label label2 = emitter.CreateLabel();
emitter.Jmp(label1);
emitter.Bind(label2);
emitter.MoviWide(0, 1);
emitter.ReturnVoid();
emitter.Bind(label1);
emit(&emitter, label2);
emitter.MoviWide(0, -1);
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(v);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetVReg(0).GetLong(), r) << ss.str();
if (ManagedThread::GetCurrent()->GetLanguageContext().GetLanguage() != panda_file::SourceLang::ECMASCRIPT) {
EXPECT_EQ(method->GetHotnessCounter(), r == 1 ? 1U : 0U) << ss.str();
}
}
}
TEST_F(InterpreterTest, TestConditionalJumps)
{
// Test jmpz
TestConditionalJmpz("jeqz", 0, 1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jeqz(label); });
TestConditionalJmpz("jeqz", 1, -1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jeqz(label); });
TestConditionalJmpz("jeqz", -1, -1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jeqz(label); });
TestConditionalJmpz("jnez", 0, -1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jnez(label); });
TestConditionalJmpz("jnez", 1, 1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jnez(label); });
TestConditionalJmpz("jnez", -1, 1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jnez(label); });
TestConditionalJmpz("jltz", -1, 1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jltz(label); });
TestConditionalJmpz("jltz", 0, -1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jltz(label); });
TestConditionalJmpz("jltz", 1, -1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jltz(label); });
TestConditionalJmpz("jgtz", 1, 1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jgtz(label); });
TestConditionalJmpz("jgtz", 0, -1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jgtz(label); });
TestConditionalJmpz("jgtz", -1, -1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jgtz(label); });
TestConditionalJmpz("jlez", -1, 1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jlez(label); });
TestConditionalJmpz("jlez", 0, 1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jlez(label); });
TestConditionalJmpz("jlez", 1, -1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jlez(label); });
TestConditionalJmpz("jgez", 1, 1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jgez(label); });
TestConditionalJmpz("jgez", 0, 1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jgez(label); });
TestConditionalJmpz("jgez", -1, -1, [](BytecodeEmitter *emitter, const Label &label) { emitter->Jgez(label); });
// Test jmp
TestConditionalJmp("jeq", 2, 2, 1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jeq(reg, label); });
TestConditionalJmp("jeq", 1, 2, -1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jeq(reg, label); });
TestConditionalJmp("jeq", 2, 1, -1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jeq(reg, label); });
TestConditionalJmp("jne", 2, 2, -1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jne(reg, label); });
TestConditionalJmp("jne", 1, 2, 1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jne(reg, label); });
TestConditionalJmp("jne", 2, 1, 1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jne(reg, label); });
TestConditionalJmp("jlt", 2, 2, -1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jlt(reg, label); });
TestConditionalJmp("jlt", 1, 2, 1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jlt(reg, label); });
TestConditionalJmp("jlt", 2, 1, -1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jlt(reg, label); });
TestConditionalJmp("jgt", 2, 2, -1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jgt(reg, label); });
TestConditionalJmp("jgt", 1, 2, -1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jgt(reg, label); });
TestConditionalJmp("jgt", 2, 1, 1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jgt(reg, label); });
TestConditionalJmp("jle", 2, 2, 1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jle(reg, label); });
TestConditionalJmp("jle", 1, 2, 1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jle(reg, label); });
TestConditionalJmp("jle", 2, 1, -1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jle(reg, label); });
TestConditionalJmp("jge", 2, 2, 1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jge(reg, label); });
TestConditionalJmp("jge", 1, 2, -1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jge(reg, label); });
TestConditionalJmp("jge", 2, 1, 1,
[](BytecodeEmitter *emitter, uint8_t reg, const Label &label) { emitter->Jge(reg, label); });
}
template <class T>
void TestBinOp2(const std::string &mnemonic, T v1, T v2, T r, std::function<void(BytecodeEmitter *, uint8_t)> emit,
bool is_div = false)
{
std::ostringstream ss;
ss << "Test " << mnemonic << " with sizeof(T) = " << sizeof(T);
ss << ", v1 = " << v1 << ", v2 = " << v2;
BytecodeEmitter emitter;
emit(&emitter, 0);
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
bool is_arithmetic_exception_expected = is_div && v2 == 0;
if (is_arithmetic_exception_expected) {
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
}
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(v1);
f->GetVReg(0).SetPrimitive(v2);
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
if (is_arithmetic_exception_expected) {
RuntimeInterface::SetArithmeticExceptionData({true});
thread->SetException(exception);
}
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
if (is_arithmetic_exception_expected) {
RuntimeInterface::SetArithmeticExceptionData({false});
auto *curr_thread = ManagedThread::GetCurrent();
ASSERT_FALSE(curr_thread->HasPendingException()) << ss.str();
ASSERT_EQ(f->GetAcc().GetReference(), exception) << ss.str();
} else {
EXPECT_EQ(f->GetAcc().GetAs<T>(), r) << ss.str();
}
}
TEST_F(InterpreterTest, TestBinOp2)
{
constexpr size_t BITWIDTH = std::numeric_limits<uint64_t>::digits;
constexpr int64_t I32_MAX = std::numeric_limits<int32_t>::max();
constexpr int64_t I16_MAX = std::numeric_limits<int16_t>::max();
TestBinOp2<int64_t>("add2", I32_MAX, 2, I32_MAX + 2,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Add2Wide(reg); });
TestBinOp2<int32_t>("add2", I16_MAX, 2, I16_MAX + 2,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Add2(reg); });
TestBinOp2<double>("fadd2", 1.0, 2.0, 1.0 + 2.0,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Fadd2Wide(reg); });
TestBinOp2<int64_t>("sub2", 1, 2, 1 - 2, [](BytecodeEmitter *emitter, uint8_t reg) { emitter->Sub2Wide(reg); });
TestBinOp2<int32_t>("sub2", 1, 2, 1 - 2, [](BytecodeEmitter *emitter, uint8_t reg) { emitter->Sub2(reg); });
TestBinOp2<double>("fsub2", 1.0, 2.0, 1.0 - 2.0,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Fsub2Wide(reg); });
TestBinOp2<int64_t>("mul2", I32_MAX, 3, I32_MAX * 3,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Mul2Wide(reg); });
TestBinOp2<int32_t>("mul2", I16_MAX, 3, I16_MAX * 3,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Mul2(reg); });
TestBinOp2<double>("fmul2", 2.0, 3.0, 2.0 * 3.0,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Fmul2Wide(reg); });
TestBinOp2<double>("fdiv2", 5.0, 2.0, 5.0 / 2.0,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Fdiv2Wide(reg); });
TestBinOp2<double>("fmod2", 10.0, 3.3, fmod(10.0, 3.3),
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Fmod2Wide(reg); });
TestBinOp2<int64_t>("and2", 0xaabbccdd11223344, 0xffffffff00000000, 0xaabbccdd00000000,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->And2Wide(reg); });
TestBinOp2<int64_t>("or2", 0xaabbccdd, 0xffff00000000, 0xffffaabbccdd,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Or2Wide(reg); });
TestBinOp2<int64_t>("xor2", 0xaabbccdd11223344, 0xffffffffffffffff, 0xaabbccdd11223344 ^ 0xffffffffffffffff,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Xor2Wide(reg); });
TestBinOp2<int64_t>("shl2", 0xaabbccdd, 16, 0xaabbccdd0000,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Shl2Wide(reg); });
TestBinOp2<int64_t>("shl2", 0xaabbccdd, BITWIDTH + 16, 0xaabbccdd0000,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Shl2Wide(reg); });
TestBinOp2<int64_t>("shr2", 0xaabbccdd11223344, 32, 0xaabbccdd,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Shr2Wide(reg); });
TestBinOp2<int64_t>("shr2", 0xaabbccdd11223344, BITWIDTH + 32, 0xaabbccdd,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Shr2Wide(reg); });
TestBinOp2<int64_t>("ashr2", 0xaabbccdd11223344, 32, 0xffffffffaabbccdd,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Ashr2Wide(reg); });
TestBinOp2<int64_t>("ashr2", 0xaabbccdd11223344, BITWIDTH + 32, 0xffffffffaabbccdd,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Ashr2Wide(reg); });
TestBinOp2<int64_t>(
"div2", 0xabbccdd11223344, 32, 0x55de66e889119a,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Div2Wide(reg); }, true);
TestBinOp2<int64_t>(
"div2", 0xabbccdd11223344, 0, 0, [](BytecodeEmitter *emitter, uint8_t reg) { emitter->Div2Wide(reg); }, true);
TestBinOp2<int64_t>(
"mod2", 0xabbccdd11223344, 32, 4, [](BytecodeEmitter *emitter, uint8_t reg) { emitter->Mod2Wide(reg); }, true);
TestBinOp2<int64_t>(
"mod2", 0xabbccdd11223344, 0, 0, [](BytecodeEmitter *emitter, uint8_t reg) { emitter->Mod2Wide(reg); }, true);
TestBinOp2<int32_t>("and", 0xaabbccdd, 0xffff, 0xccdd,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->And2(reg); });
TestBinOp2<int32_t>("or", 0xaabbccdd, 0xffff, 0xaabbffff,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Or2(reg); });
TestBinOp2<int32_t>("xor2", 0xaabbccdd, 0xffffffff, 0xaabbccdd ^ 0xffffffff,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Xor2(reg); });
TestBinOp2<int32_t>("shl2", 0xaabbccdd, 16, 0xccdd0000,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Shl2(reg); });
TestBinOp2<int32_t>("shl2", 0xaabbccdd, BITWIDTH + 16, 0xccdd0000,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Shl2(reg); });
TestBinOp2<int32_t>("shr2", 0xaabbccdd, 16, 0xaabb,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Shr2(reg); });
TestBinOp2<int32_t>("shr2", 0xaabbccdd, BITWIDTH + 16, 0xaabb,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Shr2(reg); });
TestBinOp2<int32_t>("ashr2", 0xaabbccdd, 16, 0xffffaabb,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Ashr2(reg); });
TestBinOp2<int32_t>("ashr2", 0xaabbccdd, BITWIDTH + 16, 0xffffaabb,
[](BytecodeEmitter *emitter, uint8_t reg) { emitter->Ashr2(reg); });
TestBinOp2<int32_t>(
"div2", 0xabbccdd, 16, 0xabbccd, [](BytecodeEmitter *emitter, uint8_t reg) { emitter->Div2(reg); }, true);
TestBinOp2<int32_t>(
"div2", 0xabbccdd, 0, 0, [](BytecodeEmitter *emitter, uint8_t reg) { emitter->Div2(reg); }, true);
TestBinOp2<int32_t>(
"mod2", 0xabbccdd, 16, 0xd, [](BytecodeEmitter *emitter, uint8_t reg) { emitter->Mod2(reg); }, true);
TestBinOp2<int32_t>(
"mod2", 0xabbccdd, 0, 0, [](BytecodeEmitter *emitter, uint8_t reg) { emitter->Mod2(reg); }, true);
}
template <class T>
void TestBinOp(const std::string &mnemonic, T v1, T v2, T r,
std::function<void(BytecodeEmitter *, uint8_t, uint8_t)> emit, bool is_div = false)
{
std::ostringstream ss;
ss << "Test " << mnemonic << " with sizeof(T) = " << sizeof(T);
ss << ", v1 = " << v1 << ", v2 = " << v2;
BytecodeEmitter emitter;
emit(&emitter, 0, 1);
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
bool is_arithmetic_exception_expected = is_div && v2 == 0;
if (is_arithmetic_exception_expected) {
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
}
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetVReg(0).SetPrimitive(v1);
f->GetVReg(1).SetPrimitive(v2);
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
if (is_arithmetic_exception_expected) {
RuntimeInterface::SetArithmeticExceptionData({true});
thread->SetException(exception);
}
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
if (is_arithmetic_exception_expected) {
RuntimeInterface::SetArithmeticExceptionData({false});
auto *curr_thread = ManagedThread::GetCurrent();
ASSERT_FALSE(curr_thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
} else {
EXPECT_EQ(f->GetAcc().GetAs<T>(), r) << ss.str();
}
}
TEST_F(InterpreterTest, TestBinOp)
{
constexpr size_t BITWIDTH = std::numeric_limits<uint32_t>::digits;
constexpr int64_t I16_MAX = std::numeric_limits<int16_t>::max();
TestBinOp<int32_t>("add", I16_MAX, 2, I16_MAX + 2,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Add(reg1, reg2); });
TestBinOp<int32_t>("sub", 1, 2, 1 - 2U,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Sub(reg1, reg2); });
TestBinOp<int32_t>("mul", I16_MAX, 3, I16_MAX * 3,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Mul(reg1, reg2); });
TestBinOp<int32_t>("and", 0xaabbccdd, 0xffff, 0xccdd,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->And(reg1, reg2); });
TestBinOp<int32_t>("or", 0xaabbccdd, 0xffff, 0xaabbffff,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Or(reg1, reg2); });
TestBinOp<int32_t>("xor", 0xaabbccdd, 0xffffffff, 0xaabbccdd ^ 0xffffffff,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Xor(reg1, reg2); });
TestBinOp<int32_t>("shl", 0xaabbccdd, 16, 0xccdd0000,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Shl(reg1, reg2); });
TestBinOp<int32_t>("shl", 0xaabbccdd, BITWIDTH + 16, 0xccdd0000,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Shl(reg1, reg2); });
TestBinOp<int32_t>("shr", 0xaabbccdd, 16, 0xaabb,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Shr(reg1, reg2); });
TestBinOp<int32_t>("shr", 0xaabbccdd, BITWIDTH + 16, 0xaabb,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Shr(reg1, reg2); });
TestBinOp<int32_t>("ashr", 0xaabbccdd, 16, 0xffffaabb,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Ashr(reg1, reg2); });
TestBinOp<int32_t>("ashr", 0xaabbccdd, BITWIDTH + 16, 0xffffaabb,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Ashr(reg1, reg2); });
TestBinOp<int32_t>(
"div", 0xabbccdd, 16, 0xabbccd,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Div(reg1, reg2); }, true);
TestBinOp<int32_t>(
"div", 0xabbccdd, 0, 0, [](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Div(reg1, reg2); },
true);
TestBinOp<int32_t>(
"mod", 0xabbccdd, 16, 0xd,
[](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Mod(reg1, reg2); }, true);
TestBinOp<int32_t>(
"mod", 0xabbccdd, 0, 0, [](BytecodeEmitter *emitter, uint8_t reg1, uint8_t reg2) { emitter->Mod(reg1, reg2); },
true);
}
void TestBinOpImm(const std::string &mnemonic, int32_t v1, int8_t v2, int32_t r,
std::function<void(BytecodeEmitter *, int8_t)> emit, bool is_div = false)
{
std::ostringstream ss;
ss << "Test " << mnemonic << " with v1 = " << v1 << ", v2 = " << static_cast<int32_t>(v2);
BytecodeEmitter emitter;
emit(&emitter, v2);
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
bool is_arithmetic_exception_expected = is_div && v2 == 0;
if (is_arithmetic_exception_expected) {
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
}
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(v1);
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
if (is_arithmetic_exception_expected) {
RuntimeInterface::SetArithmeticExceptionData({true});
thread->SetException(exception);
}
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
if (is_arithmetic_exception_expected) {
RuntimeInterface::SetArithmeticExceptionData({false});
auto *curr_thread = ManagedThread::GetCurrent();
ASSERT_FALSE(curr_thread->HasPendingException()) << ss.str();
ASSERT_EQ(f->GetAcc().GetReference(), exception) << ss.str();
} else {
EXPECT_EQ(f->GetAcc().Get(), r) << ss.str();
}
}
TEST_F(InterpreterTest, TestBinOpImm)
{
constexpr size_t BITWIDTH = std::numeric_limits<uint32_t>::digits;
constexpr int64_t I16_MAX = std::numeric_limits<int16_t>::max();
TestBinOpImm("addi", I16_MAX, 2, I16_MAX + 2, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Addi(imm); });
TestBinOpImm("subi", 1, 2, 1 - 2U, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Subi(imm); });
TestBinOpImm("muli", I16_MAX, 3, I16_MAX * 3, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Muli(imm); });
TestBinOpImm("andi", 0xaabbccdd, 0xf, 0xd, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Andi(imm); });
TestBinOpImm("ori", 0xaabbccdd, 0xf, 0xaabbccdf, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Ori(imm); });
TestBinOpImm("xori", 0xaabbccdd, 0xf, 0xaabbccdd ^ 0xf,
[](BytecodeEmitter *emitter, int8_t imm) { emitter->Xori(imm); });
TestBinOpImm("shli", 0xaabbccdd, 16, 0xccdd0000, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Shli(imm); });
TestBinOpImm("shli", 0xaabbccdd, BITWIDTH + 16, 0xccdd0000,
[](BytecodeEmitter *emitter, int8_t imm) { emitter->Shli(imm); });
TestBinOpImm("shri", 0xaabbccdd, 16, 0xaabb, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Shri(imm); });
TestBinOpImm("shri", 0xaabbccdd, BITWIDTH + 16, 0xaabb,
[](BytecodeEmitter *emitter, int8_t imm) { emitter->Shri(imm); });
TestBinOpImm("ashri", 0xaabbccdd, 16, 0xffffaabb,
[](BytecodeEmitter *emitter, int8_t imm) { emitter->Ashri(imm); });
TestBinOpImm("ashri", 0xaabbccdd, BITWIDTH + 16, 0xffffaabb,
[](BytecodeEmitter *emitter, int8_t imm) { emitter->Ashri(imm); });
TestBinOpImm(
"divi", 0xabbccdd, 16, 0xabbccd, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Divi(imm); }, true);
TestBinOpImm(
"divi", 0xabbccdd, 0, 0, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Divi(imm); }, true);
TestBinOpImm(
"modi", 0xabbccdd, 16, 0xd, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Modi(imm); }, true);
TestBinOpImm(
"modi", 0xabbccdd, 0, 0, [](BytecodeEmitter *emitter, int8_t imm) { emitter->Modi(imm); }, true);
}
template <class T, class R>
void TestUnaryOp(const std::string &mnemonic, T v, R r, std::function<void(BytecodeEmitter *)> emit)
{
std::ostringstream ss;
ss << "Test " << mnemonic << " with v = " << v;
BytecodeEmitter emitter;
emit(&emitter);
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(v);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetAcc().GetAs<R>(), r) << ss.str();
}
TEST_F(InterpreterTest, TestUnaryOp)
{
constexpr int64_t I32_MIN = std::numeric_limits<int32_t>::min();
constexpr int64_t I64_MIN = std::numeric_limits<int64_t>::min();
TestUnaryOp<int64_t, int64_t>("neg", I64_MIN + 1, -(I64_MIN + 1),
[](BytecodeEmitter *emitter) { emitter->NegWide(); });
TestUnaryOp<int32_t, int64_t>("neg", I32_MIN + 1, -(I32_MIN + 1), [](BytecodeEmitter *emitter) { emitter->Neg(); });
TestUnaryOp<double, double>("fneg", 1.0, -1.0, [](BytecodeEmitter *emitter) { emitter->FnegWide(); });
TestUnaryOp<int64_t, int64_t>("not", 0, 0xffffffffffffffff, [](BytecodeEmitter *emitter) { emitter->NotWide(); });
TestUnaryOp<int32_t, int32_t>("not", 0, 0xffffffff, [](BytecodeEmitter *emitter) { emitter->Not(); });
}
TEST_F(InterpreterTest, TestInci)
{
BytecodeEmitter emitter;
constexpr int32_t R0_VALUE = 2;
constexpr int32_t R1_VALUE = -3;
emitter.Inci(0, R0_VALUE);
emitter.Inci(1, R1_VALUE);
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetVReg(0).SetPrimitive(-R0_VALUE);
f->GetVReg(1).SetPrimitive(-R1_VALUE);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetVReg(0).GetAs<int32_t>(), 0);
EXPECT_EQ(f->GetVReg(1).GetAs<int32_t>(), 0);
}
TEST_F(InterpreterTest, TestCast)
{
constexpr int64_t I64_MAX = std::numeric_limits<int64_t>::max();
constexpr int32_t I32_MAX = std::numeric_limits<int32_t>::max();
constexpr int64_t I64_MIN = std::numeric_limits<int64_t>::min();
constexpr int32_t I32_MIN = std::numeric_limits<int32_t>::min();
constexpr double F64_MAX = std::numeric_limits<double>::max();
constexpr double F64_PINF = std::numeric_limits<double>::infinity();
constexpr double F64_NINF = -F64_PINF;
double f64 = 64.0;
TestUnaryOp("i32toi64", I32_MAX, static_cast<int64_t>(I32_MAX),
[](BytecodeEmitter *emitter) { emitter->I32toi64(); });
TestUnaryOp("i32tof64", I32_MAX, static_cast<double>(I32_MAX),
[](BytecodeEmitter *emitter) { emitter->I32tof64(); });
TestUnaryOp("i64toi32", I64_MAX, static_cast<int32_t>(I64_MAX),
[](BytecodeEmitter *emitter) { emitter->I64toi32(); });
TestUnaryOp("i64tof64", I64_MAX, static_cast<double>(I64_MAX),
[](BytecodeEmitter *emitter) { emitter->I64tof64(); });
TestUnaryOp("F64toi32", F64_MAX, I32_MAX, [](BytecodeEmitter *emitter) { emitter->F64toi32(); });
TestUnaryOp("F64toi32", F64_PINF, I32_MAX, [](BytecodeEmitter *emitter) { emitter->F64toi32(); });
TestUnaryOp("F64toi32", -F64_MAX, I32_MIN, [](BytecodeEmitter *emitter) { emitter->F64toi32(); });
TestUnaryOp("F64toi32", F64_NINF, I32_MIN, [](BytecodeEmitter *emitter) { emitter->F64toi32(); });
TestUnaryOp("F64toi32", nan(""), 0, [](BytecodeEmitter *emitter) { emitter->F64toi32(); });
TestUnaryOp("F64toi32", f64, static_cast<int32_t>(f64), [](BytecodeEmitter *emitter) { emitter->F64toi32(); });
TestUnaryOp("F64toi64", F64_MAX, I64_MAX, [](BytecodeEmitter *emitter) { emitter->F64toi64(); });
TestUnaryOp("F64toi64", F64_PINF, I64_MAX, [](BytecodeEmitter *emitter) { emitter->F64toi64(); });
TestUnaryOp("F64toi64", -F64_MAX, I64_MIN, [](BytecodeEmitter *emitter) { emitter->F64toi64(); });
TestUnaryOp("F64toi64", F64_NINF, I64_MIN, [](BytecodeEmitter *emitter) { emitter->F64toi64(); });
TestUnaryOp("F64toi64", nan(""), 0, [](BytecodeEmitter *emitter) { emitter->F64toi64(); });
TestUnaryOp("F64toi64", f64, static_cast<int64_t>(f64), [](BytecodeEmitter *emitter) { emitter->F64toi64(); });
}
// clang-format off
template <panda_file::Type::TypeId type_id>
struct ArrayComponentTypeHelper {
using type = std::conditional_t<type_id == panda_file::Type::TypeId::U1, uint8_t,
std::conditional_t<type_id == panda_file::Type::TypeId::I8, int8_t,
std::conditional_t<type_id == panda_file::Type::TypeId::U8, uint8_t,
std::conditional_t<type_id == panda_file::Type::TypeId::I16, int16_t,
std::conditional_t<type_id == panda_file::Type::TypeId::U16, uint16_t,
std::conditional_t<type_id == panda_file::Type::TypeId::I32, int32_t,
std::conditional_t<type_id == panda_file::Type::TypeId::U32, uint32_t,
std::conditional_t<type_id == panda_file::Type::TypeId::I64, int64_t,
std::conditional_t<type_id == panda_file::Type::TypeId::U64, uint64_t,
std::conditional_t<type_id == panda_file::Type::TypeId::F32, float,
std::conditional_t<type_id == panda_file::Type::TypeId::F64, double,
std::conditional_t<type_id == panda_file::Type::TypeId::REFERENCE, ObjectHeader*, void>>>>>>>>>>>>;
};
// clang-format on
template <panda_file::Type::TypeId type_id>
using ArrayComponentTypeHelperT = typename ArrayComponentTypeHelper<type_id>::type;
template <panda_file::Type::TypeId type_id>
struct ArrayStoredTypeHelperT {
using type = typename ArrayComponentTypeHelper<type_id>::type;
};
template <>
struct ArrayStoredTypeHelperT<panda_file::Type::TypeId::REFERENCE> {
using type = object_pointer_type;
};
template <panda_file::Type::TypeId type_id>
typename ArrayStoredTypeHelperT<type_id>::type CastIfRef(ArrayComponentTypeHelperT<type_id> value)
{
if constexpr (type_id == panda_file::Type::TypeId::REFERENCE) {
return static_cast<object_pointer_type>(reinterpret_cast<uintptr_t>(value));
} else {
return value;
}
}
coretypes::Array *AllocArray(Class *cls, [[maybe_unused]] size_t elem_size, size_t length)
{
return coretypes::Array::Create(cls, length);
}
ObjectHeader *AllocObject(Class *cls)
{
return ObjectHeader::Create(cls);
}
template <class T>
static T GetStoreValue([[maybe_unused]] Class *cls)
{
if constexpr (std::is_same_v<T, ObjectHeader *>) {
return AllocObject(cls);
}
return std::numeric_limits<T>::max();
}
template <class T>
static T GetLoadValue([[maybe_unused]] Class *cls)
{
if constexpr (std::is_same_v<T, ObjectHeader *>) {
return AllocObject(cls);
}
return std::numeric_limits<T>::min() + 1;
}
PandaString GetArrayClassName(panda_file::Type::TypeId component_type_id)
{
PandaString descriptor;
if (component_type_id == panda_file::Type::TypeId::REFERENCE) {
ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("panda.Object"), 1, &descriptor);
return descriptor;
}
ClassHelper::GetPrimitiveArrayDescriptor(panda_file::Type(component_type_id), 1, &descriptor);
return descriptor;
}
template <panda_file::Type::TypeId component_type_id>
static void TestArray()
{
std::ostringstream ss;
ss << "Test with component type id " << static_cast<uint32_t>(component_type_id);
using component_type = ArrayComponentTypeHelperT<component_type_id>;
using stored_type = typename ArrayStoredTypeHelperT<component_type_id>::type;
BytecodeEmitter emitter;
constexpr int64_t ARRAY_LENGTH = 10;
constexpr size_t STORE_IDX = ARRAY_LENGTH - 1;
constexpr size_t LOAD_IDX = 0;
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr) << ss.str();
auto ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
PandaString array_class_name = GetArrayClassName(component_type_id);
Class *array_class = class_linker->GetExtension(ctx)->GetClass(utf::CStringAsMutf8(array_class_name.c_str()));
Class *elem_class = array_class->GetComponentType();
const component_type STORE_VALUE = GetStoreValue<component_type>(elem_class);
const component_type LOAD_VALUE = GetLoadValue<component_type>(elem_class);
emitter.Movi(0, ARRAY_LENGTH);
emitter.Newarr(1, 0, RuntimeInterface::TYPE_ID.AsIndex());
if constexpr (component_type_id == panda_file::Type::TypeId::REFERENCE) {
emitter.LdaObj(4);
} else if constexpr (component_type_id == panda_file::Type::TypeId::F32) {
emitter.Fldai(bit_cast<int32_t>(STORE_VALUE));
} else if constexpr (component_type_id == panda_file::Type::TypeId::F64) {
emitter.FldaiWide(bit_cast<int64_t>(STORE_VALUE));
} else {
emitter.LdaiWide(static_cast<int64_t>(STORE_VALUE));
}
emitter.Movi(2U, STORE_IDX);
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
switch (component_type_id) {
case panda_file::Type::TypeId::U1:
case panda_file::Type::TypeId::U8: {
emitter.Starr8(1, 2U);
emitter.Ldai(LOAD_IDX);
emitter.Ldarru8(1);
break;
}
case panda_file::Type::TypeId::I8: {
emitter.Starr8(1, 2U);
emitter.Ldai(LOAD_IDX);
emitter.Ldarr8(1);
break;
}
case panda_file::Type::TypeId::U16: {
emitter.Starr16(1, 2U);
emitter.Ldai(LOAD_IDX);
emitter.Ldarru16(1);
break;
}
case panda_file::Type::TypeId::I16: {
emitter.Starr16(1, 2U);
emitter.Ldai(LOAD_IDX);
emitter.Ldarr16(1);
break;
}
case panda_file::Type::TypeId::U32:
case panda_file::Type::TypeId::I32: {
emitter.Starr(1, 2U);
emitter.Ldai(LOAD_IDX);
emitter.Ldarr(1);
break;
}
case panda_file::Type::TypeId::U64:
case panda_file::Type::TypeId::I64: {
emitter.StarrWide(1, 2U);
emitter.Ldai(LOAD_IDX);
emitter.LdarrWide(1);
break;
}
case panda_file::Type::TypeId::F32: {
emitter.Fstarr32(1, 2U);
emitter.Ldai(LOAD_IDX);
emitter.Fldarr32(1);
break;
}
case panda_file::Type::TypeId::F64: {
emitter.FstarrWide(1, 2U);
emitter.Ldai(LOAD_IDX);
emitter.FldarrWide(1);
break;
}
default: {
UNREACHABLE();
break;
}
}
} else {
emitter.StarrObj(1, 2U);
emitter.Ldai(LOAD_IDX);
emitter.LdarrObj(1);
}
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
emitter.StaWide(3U);
} else {
emitter.StaObj(3U);
}
emitter.Lenarr(1);
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
if constexpr (component_type_id == panda_file::Type::TypeId::REFERENCE) {
f->GetVReg(4U).SetReference(STORE_VALUE);
}
coretypes::Array *array = AllocArray(array_class, sizeof(stored_type), ARRAY_LENGTH);
array->Set<component_type>(LOAD_IDX, LOAD_VALUE);
RuntimeInterface::SetupResolvedClass(array_class);
RuntimeInterface::SetupArrayClass(array_class);
RuntimeInterface::SetupArrayLength(ARRAY_LENGTH);
RuntimeInterface::SetupArrayObject(array);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupArrayClass(nullptr);
RuntimeInterface::SetupArrayObject(nullptr);
ASSERT_EQ(f->GetAcc().Get(), ARRAY_LENGTH) << ss.str();
auto *result = static_cast<coretypes::Array *>(f->GetVReg(1).GetReference());
EXPECT_EQ(result, array) << ss.str();
EXPECT_EQ(f->GetVReg(3U).GetAs<component_type>(), LOAD_VALUE) << ss.str();
std::vector<stored_type> data(ARRAY_LENGTH);
data[LOAD_IDX] = CastIfRef<component_type_id>(LOAD_VALUE);
data[STORE_IDX] = CastIfRef<component_type_id>(STORE_VALUE);
EXPECT_THAT(data, ::testing::ElementsAreArray(reinterpret_cast<stored_type *>(array->GetData()), ARRAY_LENGTH))
<< ss.str();
}
TEST_F(InterpreterTest, TestArray)
{
TestArray<panda_file::Type::TypeId::U1>();
TestArray<panda_file::Type::TypeId::I8>();
TestArray<panda_file::Type::TypeId::U8>();
TestArray<panda_file::Type::TypeId::I16>();
TestArray<panda_file::Type::TypeId::U16>();
TestArray<panda_file::Type::TypeId::I32>();
TestArray<panda_file::Type::TypeId::U32>();
TestArray<panda_file::Type::TypeId::I64>();
TestArray<panda_file::Type::TypeId::U64>();
TestArray<panda_file::Type::TypeId::F32>();
TestArray<panda_file::Type::TypeId::F64>();
TestArray<panda_file::Type::TypeId::REFERENCE>();
}
void TestNewArrayExceptions()
{
// Test with negative size
{
BytecodeEmitter emitter;
emitter.Movi(0, -1);
emitter.Newarr(0, 0, RuntimeInterface::TYPE_ID.AsIndex());
emitter.Movi(0, 0);
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
RuntimeInterface::SetNegativeArraySizeExceptionData({true, -1});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetNegativeArraySizeExceptionData({false, 0});
ASSERT_FALSE(thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
}
// Test with zero size
{
BytecodeEmitter emitter;
emitter.Movi(0, 0);
emitter.Newarr(0, 0, RuntimeInterface::TYPE_ID.AsIndex());
emitter.LdaObj(0);
emitter.ReturnObj();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *array_class = class_linker->GetExtension(ctx)->GetClassRoot(ClassRoot::ARRAY_U1);
coretypes::Array *array = AllocArray(array_class, 1, 0);
RuntimeInterface::SetupResolvedClass(array_class);
RuntimeInterface::SetupArrayClass(array_class);
RuntimeInterface::SetupArrayLength(0);
RuntimeInterface::SetupArrayObject(array);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupArrayClass(nullptr);
RuntimeInterface::SetupArrayObject(nullptr);
EXPECT_EQ(array, f->GetAcc().GetReference());
}
}
template <panda_file::Type::TypeId component_type_id>
void TestLoadArrayExceptions()
{
std::ostringstream ss;
ss << "Test with component type id " << static_cast<uint32_t>(component_type_id);
using component_type = ArrayComponentTypeHelperT<component_type_id>;
constexpr int32_t ARRAY_LENGTH = 10;
// Test NullPointerException
{
BytecodeEmitter emitter;
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
switch (sizeof(component_type)) {
case sizeof(uint8_t): {
emitter.Ldarr8(0);
break;
}
case sizeof(uint16_t): {
emitter.Ldarr16(0);
break;
}
case sizeof(uint32_t): {
emitter.Ldarr(0);
break;
}
case sizeof(uint64_t): {
emitter.LdarrWide(0);
break;
}
default: {
UNREACHABLE();
break;
}
}
} else {
emitter.LdarrObj(0);
}
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetVReg(0).SetReference(nullptr);
f->GetAcc().SetPrimitive(-1);
RuntimeInterface::SetNullPointerExceptionData({true});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetNullPointerExceptionData({false});
ASSERT_FALSE(thread->HasPendingException()) << ss.str();
ASSERT_EQ(f->GetAcc().GetReference(), exception) << ss.str();
}
// Test OOB exception
{
BytecodeEmitter emitter;
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
switch (sizeof(component_type)) {
case sizeof(uint8_t): {
emitter.Ldarr8(0);
break;
}
case sizeof(uint16_t): {
emitter.Ldarr16(0);
break;
}
case sizeof(uint32_t): {
emitter.Ldarr(0);
break;
}
case sizeof(uint64_t): {
emitter.LdarrWide(0);
break;
}
default: {
UNREACHABLE();
break;
}
}
} else {
emitter.LdarrObj(0);
}
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr) << ss.str();
PandaString array_class_name = GetArrayClassName(component_type_id);
auto ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *array_class = class_linker->GetExtension(ctx)->GetClass(utf::CStringAsMutf8(array_class_name.c_str()));
coretypes::Array *array = AllocArray(array_class, sizeof(component_type), ARRAY_LENGTH);
f->GetVReg(0).SetReference(array);
f->GetAcc().SetPrimitive(-1);
RuntimeInterface::SetArrayIndexOutOfBoundsExceptionData({true, -1, ARRAY_LENGTH});
RuntimeInterface::SetupResolvedClass(array_class);
RuntimeInterface::SetupArrayClass(array_class);
RuntimeInterface::SetupArrayLength(ARRAY_LENGTH);
RuntimeInterface::SetupArrayObject(array);
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupArrayClass(nullptr);
RuntimeInterface::SetupArrayObject(nullptr);
RuntimeInterface::SetArrayIndexOutOfBoundsExceptionData({false, 0, 0});
ASSERT_FALSE(thread->HasPendingException()) << ss.str();
ASSERT_EQ(f->GetAcc().GetReference(), exception) << ss.str();
}
{
BytecodeEmitter emitter;
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
switch (sizeof(component_type)) {
case sizeof(uint8_t): {
emitter.Ldarr8(0);
break;
}
case sizeof(uint16_t): {
emitter.Ldarr16(0);
break;
}
case sizeof(uint32_t): {
emitter.Ldarr(0);
break;
}
case sizeof(uint64_t): {
emitter.LdarrWide(0);
break;
}
default: {
UNREACHABLE();
break;
}
}
} else {
emitter.LdarrObj(0);
}
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr) << ss.str();
PandaString array_class_name = GetArrayClassName(component_type_id);
auto ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *array_class = class_linker->GetExtension(ctx)->GetClass(utf::CStringAsMutf8(array_class_name.c_str()));
coretypes::Array *array = AllocArray(array_class, sizeof(component_type), ARRAY_LENGTH);
f->GetVReg(0).SetReference(array);
f->GetAcc().SetPrimitive(ARRAY_LENGTH);
RuntimeInterface::SetArrayIndexOutOfBoundsExceptionData({true, ARRAY_LENGTH, ARRAY_LENGTH});
RuntimeInterface::SetupResolvedClass(array_class);
RuntimeInterface::SetupArrayClass(array_class);
RuntimeInterface::SetupArrayLength(ARRAY_LENGTH);
RuntimeInterface::SetupArrayObject(array);
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupArrayClass(nullptr);
RuntimeInterface::SetupArrayObject(nullptr);
RuntimeInterface::SetArrayIndexOutOfBoundsExceptionData({false, 0, 0});
ASSERT_FALSE(thread->HasPendingException()) << ss.str();
ASSERT_EQ(f->GetAcc().GetReference(), exception) << ss.str();
}
}
template <panda_file::Type::TypeId component_type_id>
void TestStoreArrayExceptions()
{
std::ostringstream ss;
ss << "Test with component type id " << static_cast<uint32_t>(component_type_id);
using component_type = ArrayComponentTypeHelperT<component_type_id>;
constexpr int32_t ARRAY_LENGTH = 10;
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr) << ss.str();
PandaString array_class_name = GetArrayClassName(component_type_id);
auto ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *array_class = class_linker->GetExtension(ctx)->GetClass(utf::CStringAsMutf8(array_class_name.c_str()));
Class *elem_class = array_class->GetComponentType();
const component_type STORE_VALUE = GetStoreValue<component_type>(elem_class);
// Test NullPointerException
{
BytecodeEmitter emitter;
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
switch (sizeof(component_type)) {
case sizeof(uint8_t): {
emitter.Starr8(0, 1);
break;
}
case sizeof(uint16_t): {
emitter.Starr16(0, 1);
break;
}
case sizeof(uint32_t): {
emitter.Starr(0, 1);
break;
}
case sizeof(uint64_t): {
emitter.StarrWide(0, 1);
break;
}
default: {
UNREACHABLE();
break;
}
}
} else {
emitter.StarrObj(0, 1);
}
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
f->GetAcc().SetPrimitive(STORE_VALUE);
} else {
f->GetAcc().SetReference(STORE_VALUE);
}
f->GetVReg(0).SetReference(nullptr);
f->GetVReg(1).SetPrimitive(-1);
RuntimeInterface::SetNullPointerExceptionData({true});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetNullPointerExceptionData({false});
ASSERT_FALSE(thread->HasPendingException()) << ss.str();
ASSERT_EQ(f->GetAcc().GetReference(), exception) << ss.str();
}
// Test OOB exception
{
BytecodeEmitter emitter;
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
switch (sizeof(component_type)) {
case sizeof(uint8_t): {
emitter.Starr8(0, 1);
break;
}
case sizeof(uint16_t): {
emitter.Starr16(0, 1);
break;
}
case sizeof(uint32_t): {
emitter.Starr(0, 1);
break;
}
case sizeof(uint64_t): {
emitter.StarrWide(0, 1);
break;
}
default: {
UNREACHABLE();
break;
}
}
} else {
emitter.StarrObj(0, 1);
}
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
coretypes::Array *array = AllocArray(array_class, sizeof(component_type), ARRAY_LENGTH);
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
f->GetAcc().SetPrimitive(STORE_VALUE);
} else {
f->GetAcc().SetReference(STORE_VALUE);
}
f->GetVReg(0).SetReference(array);
f->GetVReg(1).SetPrimitive(-1);
RuntimeInterface::SetArrayIndexOutOfBoundsExceptionData({true, -1, ARRAY_LENGTH});
RuntimeInterface::SetupResolvedClass(array_class);
RuntimeInterface::SetupArrayClass(array_class);
RuntimeInterface::SetupArrayLength(ARRAY_LENGTH);
RuntimeInterface::SetupArrayObject(array);
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupArrayClass(nullptr);
RuntimeInterface::SetupArrayObject(nullptr);
RuntimeInterface::SetArrayIndexOutOfBoundsExceptionData({false, 0, 0});
ASSERT_FALSE(thread->HasPendingException()) << ss.str();
ASSERT_EQ(f->GetAcc().GetReference(), exception) << ss.str();
}
{
BytecodeEmitter emitter;
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
switch (sizeof(component_type)) {
case sizeof(uint8_t): {
emitter.Starr8(0, 1);
break;
}
case sizeof(uint16_t): {
emitter.Starr16(0, 1);
break;
}
case sizeof(uint32_t): {
emitter.Starr(0, 1);
break;
}
case sizeof(uint64_t): {
emitter.StarrWide(0, 1);
break;
}
default: {
UNREACHABLE();
break;
}
}
} else {
emitter.StarrObj(0, 1);
}
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
coretypes::Array *array = AllocArray(array_class, sizeof(component_type), ARRAY_LENGTH);
if constexpr (component_type_id != panda_file::Type::TypeId::REFERENCE) {
f->GetAcc().SetPrimitive(STORE_VALUE);
} else {
f->GetAcc().SetReference(STORE_VALUE);
}
f->GetVReg(0).SetReference(array);
f->GetVReg(1).SetPrimitive(ARRAY_LENGTH);
RuntimeInterface::SetArrayIndexOutOfBoundsExceptionData({true, ARRAY_LENGTH, ARRAY_LENGTH});
RuntimeInterface::SetupResolvedClass(array_class);
RuntimeInterface::SetupArrayClass(array_class);
RuntimeInterface::SetupArrayLength(ARRAY_LENGTH);
RuntimeInterface::SetupArrayObject(array);
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupArrayClass(nullptr);
RuntimeInterface::SetupArrayObject(nullptr);
RuntimeInterface::SetArrayIndexOutOfBoundsExceptionData({false, 0, 0});
ASSERT_FALSE(thread->HasPendingException()) << ss.str();
ASSERT_EQ(f->GetAcc().GetReference(), exception) << ss.str();
}
}
void TestArrayLenException()
{
// Test NullPointerException
BytecodeEmitter emitter;
emitter.Lenarr(0);
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetVReg(0).SetReference(nullptr);
RuntimeInterface::SetNullPointerExceptionData({true});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetNullPointerExceptionData({false});
ASSERT_FALSE(thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
}
ObjectHeader *AllocObject(BaseClass *cls)
{
return ObjectHeader::Create(cls);
}
TEST_F(InterpreterTest, TestNewobj)
{
BytecodeEmitter emitter;
emitter.Newobj(0, RuntimeInterface::TYPE_ID.AsIndex());
emitter.LdaObj(0);
emitter.ReturnObj();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *thread = ManagedThread::GetCurrent();
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(thread, object_class));
ObjectHeader *obj = AllocObject(object_class);
RuntimeInterface::SetupResolvedClass(object_class);
RuntimeInterface::SetupObjectClass(object_class);
RuntimeInterface::SetupObject(obj);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupObjectClass(nullptr);
RuntimeInterface::SetupObject(nullptr);
EXPECT_EQ(obj, f->GetAcc().GetReference());
}
TEST_F(InterpreterTest, TestInitobj)
{
{
BytecodeEmitter emitter;
emitter.InitobjShort(0, 2, RuntimeInterface::METHOD_ID.AsIndex());
emitter.ReturnObj();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {}
.function void R.ctor(R a0, i32 a1, i32 a2) <static> {
return.void
}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *thread = ManagedThread::GetCurrent();
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(thread, object_class));
Method *ctor = object_class->GetMethods().data();
ObjectHeader *obj = AllocObject(object_class);
f->GetVReg(0).Set(10);
f->GetVReg(2U).Set(20);
bool has_errors = false;
RuntimeInterface::SetupInvokeMethodHandler(
[&]([[maybe_unused]] ManagedThread *t, Method *m, Value *args) -> Value {
if (m != ctor) {
has_errors = true;
return Value(nullptr);
}
Span<Value> sp(args, m->GetNumArgs());
if (sp[0].GetAs<ObjectHeader *>() != obj) {
has_errors = true;
return Value(nullptr);
}
if (sp[1].GetAs<int32_t>() != f->GetVReg(0).Get()) {
has_errors = true;
return Value(nullptr);
}
if (sp[2].GetAs<int32_t>() != f->GetVReg(2U).Get()) {
has_errors = true;
return Value(nullptr);
}
return Value(nullptr);
});
RuntimeInterface::SetupResolvedMethod(ctor);
RuntimeInterface::SetupResolvedClass(object_class);
RuntimeInterface::SetupObjectClass(object_class);
RuntimeInterface::SetupObject(obj);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
ASSERT_FALSE(has_errors);
RuntimeInterface::SetupInvokeMethodHandler({});
RuntimeInterface::SetupResolvedMethod(nullptr);
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupObjectClass(nullptr);
RuntimeInterface::SetupObject(nullptr);
EXPECT_EQ(obj, f->GetAcc().GetReference());
}
{
BytecodeEmitter emitter;
emitter.Initobj(0, 2, 3, 5, RuntimeInterface::METHOD_ID.AsIndex());
emitter.ReturnObj();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {}
.function void R.ctor(R a0, i32 a1, i32 a2, i32 a3, i32 a4) <static> {
return.void
}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *thread = ManagedThread::GetCurrent();
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(thread, object_class));
Method *ctor = object_class->GetMethods().data();
ObjectHeader *obj = AllocObject(object_class);
f->GetVReg(0).Set(10);
f->GetVReg(2U).Set(20);
f->GetVReg(3U).Set(30);
f->GetVReg(5U).Set(40);
bool has_errors = false;
RuntimeInterface::SetupInvokeMethodHandler(
[&]([[maybe_unused]] ManagedThread *t, Method *m, Value *args) -> Value {
if (m != ctor) {
has_errors = true;
return Value(nullptr);
}
Span<Value> sp(args, m->GetNumArgs());
if (sp[0].GetAs<ObjectHeader *>() != obj) {
has_errors = true;
return Value(nullptr);
}
if (sp[1].GetAs<int32_t>() != f->GetVReg(0).Get()) {
has_errors = true;
return Value(nullptr);
}
if (sp[2].GetAs<int32_t>() != f->GetVReg(2U).Get()) {
has_errors = true;
return Value(nullptr);
}
if (sp[3].GetAs<int32_t>() != f->GetVReg(3U).Get()) {
has_errors = true;
return Value(nullptr);
}
if (sp[4].GetAs<int32_t>() != f->GetVReg(5U).Get()) {
has_errors = true;
return Value(nullptr);
}
return Value(nullptr);
});
RuntimeInterface::SetupResolvedMethod(ctor);
RuntimeInterface::SetupResolvedClass(object_class);
RuntimeInterface::SetupObjectClass(object_class);
RuntimeInterface::SetupObject(obj);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
ASSERT_FALSE(has_errors);
RuntimeInterface::SetupInvokeMethodHandler({});
RuntimeInterface::SetupResolvedMethod(nullptr);
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupObjectClass(nullptr);
RuntimeInterface::SetupObject(nullptr);
EXPECT_EQ(obj, f->GetAcc().GetReference());
}
{
BytecodeEmitter emitter;
emitter.InitobjRange(2, RuntimeInterface::METHOD_ID.AsIndex());
emitter.ReturnObj();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {}
.function void R.ctor(R a0, i32 a1, i32 a2, i32 a3, i32 a4, i32 a5) <static> {
return.void
}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *thread = ManagedThread::GetCurrent();
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(thread, object_class));
Method *ctor = object_class->GetMethods().data();
ObjectHeader *obj = AllocObject(object_class);
f->GetVReg(2U).Set(10U);
f->GetVReg(3U).Set(20U);
f->GetVReg(4U).Set(30U);
f->GetVReg(5U).Set(40U);
f->GetVReg(6U).Set(50U);
bool has_errors = false;
RuntimeInterface::SetupInvokeMethodHandler(
[&]([[maybe_unused]] ManagedThread *t, Method *m, Value *args) -> Value {
if (m != ctor) {
has_errors = true;
return Value(nullptr);
}
Span<Value> sp(args, m->GetNumArgs());
if (sp[0].GetAs<ObjectHeader *>() != obj) {
has_errors = true;
return Value(nullptr);
}
if (sp[1].GetAs<int32_t>() != f->GetVReg(2U).Get()) {
has_errors = true;
return Value(nullptr);
}
if (sp[2].GetAs<int32_t>() != f->GetVReg(3U).Get()) {
has_errors = true;
return Value(nullptr);
}
if (sp[3].GetAs<int32_t>() != f->GetVReg(4U).Get()) {
has_errors = true;
return Value(nullptr);
}
if (sp[4].GetAs<int32_t>() != f->GetVReg(5U).Get()) {
has_errors = true;
return Value(nullptr);
}
if (sp[5].GetAs<int32_t>() != f->GetVReg(6U).Get()) {
has_errors = true;
return Value(nullptr);
}
return Value(nullptr);
});
RuntimeInterface::SetupResolvedMethod(ctor);
RuntimeInterface::SetupResolvedClass(object_class);
RuntimeInterface::SetupObjectClass(object_class);
RuntimeInterface::SetupObject(obj);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
ASSERT_FALSE(has_errors);
RuntimeInterface::SetupInvokeMethodHandler({});
RuntimeInterface::SetupResolvedMethod(nullptr);
RuntimeInterface::SetupResolvedClass(nullptr);
RuntimeInterface::SetupObjectClass(nullptr);
RuntimeInterface::SetupObject(nullptr);
EXPECT_EQ(obj, f->GetAcc().GetReference());
}
}
void TestLoadStoreField(bool is_static)
{
BytecodeEmitter emitter;
if (is_static) {
emitter.Ldstatic(RuntimeInterface::FIELD_ID.AsIndex());
emitter.StaWide(1);
emitter.LdaWide(2U);
emitter.Ststatic(RuntimeInterface::FIELD_ID.AsIndex());
emitter.Ldstatic(RuntimeInterface::FIELD_ID.AsIndex());
} else {
emitter.Ldobj(0, RuntimeInterface::FIELD_ID.AsIndex());
emitter.StaWide(1);
emitter.LdaWide(2U);
emitter.Stobj(0, RuntimeInterface::FIELD_ID.AsIndex());
emitter.Ldobj(0, RuntimeInterface::FIELD_ID.AsIndex());
}
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
std::string source;
if (is_static) {
source = R"(
.record R {
u1 sf_u1 <static>
i8 sf_i8 <static>
u8 sf_u8 <static>
i16 sf_i16 <static>
u16 sf_u16 <static>
i32 sf_i32 <static>
u32 sf_u32 <static>
i64 sf_i64 <static>
u64 sf_u64 <static>
f32 sf_f32 <static>
f64 sf_f64 <static>
}
)";
} else {
source = R"(
.record R {
u1 if_u1
i8 if_i8
u8 if_u8
i16 if_i16
u16 if_u16
i32 if_i32
u32 if_u32
i64 if_i64
u64 if_u64
f32 if_f32
f64 if_f64
}
)";
}
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(ManagedThread::GetCurrent(), object_class));
ObjectHeader *obj = nullptr;
if (!is_static) {
obj = AllocObject(object_class);
f->GetVReg(0).SetReference(obj);
}
std::vector<panda_file::Type::TypeId> types {
panda_file::Type::TypeId::U1, panda_file::Type::TypeId::I8, panda_file::Type::TypeId::U8,
panda_file::Type::TypeId::I16, panda_file::Type::TypeId::U16, panda_file::Type::TypeId::I32,
panda_file::Type::TypeId::U32, panda_file::Type::TypeId::I64, panda_file::Type::TypeId::U64,
panda_file::Type::TypeId::F32, panda_file::Type::TypeId::F64};
Span<Field> fields = is_static ? object_class->GetStaticFields() : object_class->GetInstanceFields();
for (size_t i = 0; i < fields.size(); i++) {
Field *field = &fields[i];
std::ostringstream ss;
ss << "Test field " << reinterpret_cast<const char *>(field->GetName().data);
constexpr float FLOAT_VALUE = 1.0;
constexpr double DOUBLE_VALUE = 2.0;
int64_t value = 0;
switch (field->GetType().GetId()) {
case panda_file::Type::TypeId::U1: {
value = std::numeric_limits<uint8_t>::max();
f->GetVReg(2U).SetPrimitive(value);
break;
}
case panda_file::Type::TypeId::I8: {
value = std::numeric_limits<int8_t>::min();
f->GetVReg(2U).SetPrimitive(value);
break;
}
case panda_file::Type::TypeId::U8: {
value = std::numeric_limits<uint8_t>::max();
f->GetVReg(2U).SetPrimitive(value);
break;
}
case panda_file::Type::TypeId::I16: {
value = std::numeric_limits<int16_t>::min();
f->GetVReg(2U).SetPrimitive(value);
break;
}
case panda_file::Type::TypeId::U16: {
value = std::numeric_limits<uint16_t>::max();
f->GetVReg(2U).SetPrimitive(value);
break;
}
case panda_file::Type::TypeId::I32: {
value = std::numeric_limits<int32_t>::min();
f->GetVReg(2U).SetPrimitive(value);
break;
}
case panda_file::Type::TypeId::U32: {
value = std::numeric_limits<uint32_t>::max();
f->GetVReg(2U).SetPrimitive(value);
break;
}
case panda_file::Type::TypeId::I64: {
value = std::numeric_limits<int64_t>::min();
f->GetVReg(2U).SetPrimitive(value);
break;
}
case panda_file::Type::TypeId::U64: {
value = std::numeric_limits<uint64_t>::max();
f->GetVReg(2U).SetPrimitive(value);
break;
}
case panda_file::Type::TypeId::F32: {
f->GetVReg(2U).SetPrimitive(FLOAT_VALUE);
break;
}
case panda_file::Type::TypeId::F64: {
f->GetVReg(2U).SetPrimitive(DOUBLE_VALUE);
break;
}
default: {
UNREACHABLE();
break;
}
}
RuntimeInterface::SetupResolvedField(field);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedField(nullptr);
switch (field->GetType().GetId()) {
case panda_file::Type::TypeId::F32: {
EXPECT_EQ(f->GetAcc().GetFloat(), FLOAT_VALUE) << ss.str();
break;
}
case panda_file::Type::TypeId::F64: {
EXPECT_EQ(f->GetAcc().GetDouble(), DOUBLE_VALUE) << ss.str();
break;
}
default: {
EXPECT_EQ(f->GetAcc().GetLong(), value) << ss.str();
break;
}
}
EXPECT_EQ(f->GetVReg(1).GetLong(), 0) << ss.str();
}
}
void TestLoadStoreObjectField(bool is_static)
{
BytecodeEmitter emitter;
std::ostringstream ss;
ss << "Test load/store ";
if (is_static) {
ss << "static ";
}
ss << "object field";
if (is_static) {
emitter.LdstaticObj(RuntimeInterface::FIELD_ID.AsIndex());
emitter.StaObj(1);
emitter.LdaObj(2U);
emitter.StstaticObj(RuntimeInterface::FIELD_ID.AsIndex());
emitter.LdstaticObj(RuntimeInterface::FIELD_ID.AsIndex());
} else {
emitter.LdobjObj(0, RuntimeInterface::FIELD_ID.AsIndex());
emitter.StaObj(1);
emitter.LdaObj(2U);
emitter.StobjObj(0, RuntimeInterface::FIELD_ID.AsIndex());
emitter.LdobjObj(0, RuntimeInterface::FIELD_ID.AsIndex());
}
emitter.ReturnObj();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS) << ss.str();
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
std::string source;
if (is_static) {
source = R"(
.record R {
R sf_ref <static>
}
)";
} else {
source = R"(
.record R {
R sf_ref
}
)";
}
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr) << ss.str();
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(ManagedThread::GetCurrent(), object_class)) << ss.str();
ObjectHeader *obj = nullptr;
if (!is_static) {
obj = AllocObject(object_class);
f->GetVReg(0).SetReference(obj);
}
Span<Field> fields = is_static ? object_class->GetStaticFields() : object_class->GetInstanceFields();
Field *field = &fields[0];
ObjectHeader *ref_value = ToPointer<ObjectHeader>(0xaabbccdd);
f->GetVReg(2U).SetReference(ref_value);
RuntimeInterface::SetupResolvedField(field);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedField(nullptr);
EXPECT_EQ(f->GetAcc().GetReference(), ref_value) << ss.str();
EXPECT_EQ(f->GetVReg(1).GetReference(), nullptr) << ss.str();
}
TEST_F(InterpreterTest, TestLoadStoreField)
{
TestLoadStoreField(false);
TestLoadStoreObjectField(false);
}
TEST_F(InterpreterTest, TestLoadStoreStaticField)
{
TestLoadStoreField(true);
TestLoadStoreObjectField(true);
}
TEST_F(InterpreterTest, TestObjectExceptions)
{
// Test NullPointerException
{
BytecodeEmitter emitter;
emitter.Stobj(0, RuntimeInterface::FIELD_ID.AsIndex());
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {
i32 if
}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
Field *field = object_class->GetInstanceFields().data();
f->GetVReg(0).SetReference(nullptr);
f->GetAcc().Set(0);
RuntimeInterface::SetupResolvedField(field);
RuntimeInterface::SetNullPointerExceptionData({true});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetupResolvedField(nullptr);
RuntimeInterface::SetNullPointerExceptionData({false});
ASSERT_FALSE(thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
}
{
BytecodeEmitter emitter;
emitter.StobjObj(0, RuntimeInterface::FIELD_ID.AsIndex());
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {
R if
}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
Field *field = object_class->GetInstanceFields().data();
f->GetVReg(0).SetReference(nullptr);
f->GetAcc().SetReference(nullptr);
RuntimeInterface::SetupResolvedField(field);
RuntimeInterface::SetNullPointerExceptionData({true});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetupResolvedField(nullptr);
RuntimeInterface::SetNullPointerExceptionData({false});
ASSERT_FALSE(thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
}
{
BytecodeEmitter emitter;
emitter.Ldobj(0, RuntimeInterface::FIELD_ID.AsIndex());
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {
i32 if
}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
Field *field = object_class->GetInstanceFields().data();
f->GetVReg(0).SetReference(nullptr);
RuntimeInterface::SetupResolvedField(field);
RuntimeInterface::SetNullPointerExceptionData({true});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetupResolvedField(nullptr);
RuntimeInterface::SetNullPointerExceptionData({false});
ASSERT_FALSE(thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
}
{
BytecodeEmitter emitter;
emitter.LdobjObj(0, RuntimeInterface::FIELD_ID.AsIndex());
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {
R if
}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
Field *field = object_class->GetInstanceFields().data();
f->GetVReg(0).SetReference(nullptr);
RuntimeInterface::SetupResolvedField(field);
RuntimeInterface::SetNullPointerExceptionData({true});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetupResolvedField(nullptr);
RuntimeInterface::SetNullPointerExceptionData({false});
ASSERT_FALSE(thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
}
}
TEST_F(InterpreterTest, TestArrayExceptions)
{
TestNewArrayExceptions();
TestLoadArrayExceptions<panda_file::Type::TypeId::I8>();
TestLoadArrayExceptions<panda_file::Type::TypeId::I16>();
TestLoadArrayExceptions<panda_file::Type::TypeId::I32>();
TestLoadArrayExceptions<panda_file::Type::TypeId::I64>();
TestLoadArrayExceptions<panda_file::Type::TypeId::REFERENCE>();
TestStoreArrayExceptions<panda_file::Type::TypeId::I8>();
TestStoreArrayExceptions<panda_file::Type::TypeId::I16>();
TestStoreArrayExceptions<panda_file::Type::TypeId::I32>();
TestStoreArrayExceptions<panda_file::Type::TypeId::I64>();
TestStoreArrayExceptions<panda_file::Type::TypeId::REFERENCE>();
TestArrayLenException();
}
TEST_F(InterpreterTest, TestReturns)
{
int64_t value = 0xaabbccdd11223344;
ObjectHeader *obj = ToPointer<ObjectHeader>(0xaabbccdd);
{
BytecodeEmitter emitter;
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(value);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetAcc().Get(), static_cast<int32_t>(value));
EXPECT_FALSE(f->GetAcc().HasObject());
}
{
BytecodeEmitter emitter;
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetPrimitive(value);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetAcc().GetLong(), value);
EXPECT_FALSE(f->GetAcc().HasObject());
}
{
BytecodeEmitter emitter;
emitter.ReturnObj();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetAcc().SetReference(obj);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
EXPECT_EQ(f->GetAcc().GetReference(), obj);
EXPECT_TRUE(f->GetAcc().HasObject());
}
}
TEST_F(InterpreterTest, TestCheckCast)
{
{
BytecodeEmitter emitter;
emitter.Checkcast(RuntimeInterface::TYPE_ID.AsIndex());
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *thread = ManagedThread::GetCurrent();
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(thread, object_class));
f->GetAcc().SetReference(nullptr);
RuntimeInterface::SetupResolvedClass(object_class);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
}
{
BytecodeEmitter emitter;
emitter.Checkcast(RuntimeInterface::TYPE_ID.AsIndex());
emitter.ReturnVoid();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *thread = ManagedThread::GetCurrent();
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
auto *object_class = ext->GetClass(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("R"), 2, &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(thread, object_class));
auto *obj = AllocArray(object_class, sizeof(uint8_t), 0);
f->GetAcc().SetReference(obj);
auto *dst_class = class_linker->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
ASSERT_TRUE(class_linker->InitializeClass(thread, dst_class));
RuntimeInterface::SetupResolvedClass(dst_class);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
}
}
TEST_F(InterpreterTest, TestIsInstance)
{
{
BytecodeEmitter emitter;
emitter.Isinstance(RuntimeInterface::TYPE_ID.AsIndex());
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *thread = ManagedThread::GetCurrent();
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(thread, object_class));
f->GetAcc().SetReference(nullptr);
RuntimeInterface::SetupResolvedClass(object_class);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
ASSERT_EQ(f->GetAcc().Get(), 0);
}
{
BytecodeEmitter emitter;
emitter.Isinstance(RuntimeInterface::TYPE_ID.AsIndex());
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record R {}
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *thread = ManagedThread::GetCurrent();
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
auto *object_class = ext->GetClass(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("R"), 2, &descriptor));
ASSERT_TRUE(class_linker->InitializeClass(thread, object_class));
auto *obj = AllocArray(object_class, sizeof(uint8_t), 0);
f->GetAcc().SetReference(obj);
auto *dst_class = class_linker->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
ASSERT_TRUE(class_linker->InitializeClass(thread, dst_class));
RuntimeInterface::SetupResolvedClass(dst_class);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedClass(nullptr);
ASSERT_EQ(f->GetAcc().Get(), 1);
}
}
TEST_F(InterpreterTest, TestThrow)
{
{
BytecodeEmitter emitter;
emitter.Throw(1);
emitter.Movi(0, 16);
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
f->GetVReg(1).SetReference(exception);
f->GetVReg(0).SetPrimitive(0);
RuntimeInterface::SetCatchBlockPcOffset(panda_file::INVALID_OFFSET);
Execute(thread, bytecode.data(), f.get());
ASSERT_TRUE(thread->HasPendingException());
ASSERT_EQ(thread->GetException(), exception);
ASSERT_EQ(f->GetVReg(0).Get(), 0);
thread->ClearException();
}
{
BytecodeEmitter emitter;
emitter.Throw(1);
emitter.Movi(0, 16);
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.Movi(0, 32);
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
f->GetVReg(1).SetReference(exception);
f->GetVReg(0).SetPrimitive(0);
Execute(thread, bytecode.data(), f.get());
ASSERT_FALSE(thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
ASSERT_EQ(f->GetVReg(0).Get(), 32);
}
// Test NullPointerException
{
BytecodeEmitter emitter;
emitter.Throw(1);
emitter.Movi(0, 16);
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.Movi(0, 32);
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
f->GetVReg(1).SetReference(nullptr);
f->GetVReg(0).SetPrimitive(0);
RuntimeInterface::SetNullPointerExceptionData({true});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(thread, bytecode.data(), f.get());
RuntimeInterface::SetNullPointerExceptionData({false});
ASSERT_FALSE(thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
ASSERT_EQ(f->GetVReg(0).Get(), 32);
}
}
static void MakeShorty(size_t num_args, std::vector<uint16_t> *buf)
{
static constexpr uint8_t I64 = static_cast<uint8_t>(panda_file::Type::TypeId::I64);
static constexpr size_t ELEM_SIZE = 4;
static constexpr size_t ELEM_COUNT = std::numeric_limits<uint16_t>::digits / ELEM_SIZE;
uint16_t val = 0;
uint32_t count = 1;
++num_args; // consider the return value
while (num_args > 0) {
if (count == ELEM_COUNT) {
buf->push_back(val);
val = 0;
count = 0;
}
val |= I64 << ELEM_SIZE * count;
++count;
--num_args;
}
if (count == ELEM_COUNT) {
buf->push_back(val);
val = 0;
}
buf->push_back(val);
}
template <bool is_dynamic = false>
static std::pair<PandaUniquePtr<Method>, std::unique_ptr<const panda_file::File>> CreateResolvedMethod(
Class *klass, size_t vreg_num, const std::vector<int64_t> args, std::vector<uint8_t> *bytecode,
std::vector<uint16_t> *shorty_buf)
{
BytecodeEmitter emitter;
Label label = emitter.CreateLabel();
size_t start_idx = 0;
if constexpr (is_dynamic) {
++start_idx; // skip function object
}
for (size_t i = start_idx; i < args.size(); i++) {
emitter.LdaiWide(args[i]);
emitter.Jne(vreg_num + i, label);
}
emitter.LdaiWide(1);
emitter.ReturnWide();
emitter.Bind(label);
emitter.LdaiWide(0);
emitter.ReturnWide();
[[maybe_unused]] auto res = emitter.Build(&*bytecode);
ASSERT(res == BytecodeEmitter::ErrorCode::SUCCESS);
MakeShorty(args.size(), shorty_buf);
return CreateMethod(klass, 0, args.size(), vreg_num, shorty_buf->data(), *bytecode);
}
TEST_F(InterpreterTest, TestCalls)
{
size_t vreg_num = 10;
{
BytecodeEmitter emitter;
emitter.CallShort(1, 3, RuntimeInterface::METHOD_ID.AsIndex());
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
std::vector<int64_t> args = {1, 2};
f->GetVReg(1).SetPrimitive(args[0]);
f->GetVReg(3U).SetPrimitive(args[1]);
auto klass = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(klass.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
std::vector<uint16_t> shorty_buf;
std::vector<uint8_t> method_bytecode;
auto resolved_method_data = CreateResolvedMethod(klass.get(), vreg_num, args, &method_bytecode, &shorty_buf);
auto resolved_method = std::move(resolved_method_data.first);
RuntimeInterface::SetupResolvedMethod(resolved_method.get());
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
if (ManagedThread::GetCurrent()->GetLanguageContext().GetLanguage() != panda_file::SourceLang::ECMASCRIPT) {
EXPECT_EQ(resolved_method->GetHotnessCounter(), 1U);
}
RuntimeInterface::SetupResolvedMethod(nullptr);
EXPECT_EQ(f->GetAcc().GetLong(), 1);
}
{
BytecodeEmitter emitter;
emitter.Call(1, 3, 5, 7, RuntimeInterface::METHOD_ID.AsIndex());
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
std::vector<int64_t> args = {1, 2, 3};
f->GetVReg(1).SetPrimitive(args[0]);
f->GetVReg(3U).SetPrimitive(args[1]);
f->GetVReg(5U).SetPrimitive(args[2]);
auto klass = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(klass.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
std::vector<uint16_t> shorty_buf;
std::vector<uint8_t> method_bytecode;
auto resolved_method_data = CreateResolvedMethod(klass.get(), vreg_num, args, &method_bytecode, &shorty_buf);
auto resolved_method = std::move(resolved_method_data.first);
RuntimeInterface::SetupResolvedMethod(resolved_method.get());
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
if (ManagedThread::GetCurrent()->GetLanguageContext().GetLanguage() != panda_file::SourceLang::ECMASCRIPT) {
EXPECT_EQ(resolved_method->GetHotnessCounter(), 1U);
}
RuntimeInterface::SetupResolvedMethod(nullptr);
EXPECT_EQ(f->GetAcc().GetLong(), 1);
}
{
BytecodeEmitter emitter;
emitter.CallRange(3, RuntimeInterface::METHOD_ID.AsIndex());
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
std::vector<int64_t> args = {1, 2, 3, 4, 5, 6, 7};
for (size_t i = 0; i < args.size(); i++) {
f->GetVReg(3 + i).SetPrimitive(args[i]);
}
auto klass = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(klass.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
std::vector<uint16_t> shorty_buf;
std::vector<uint8_t> method_bytecode;
auto resolved_method_data = CreateResolvedMethod(klass.get(), vreg_num, args, &method_bytecode, &shorty_buf);
auto resolved_method = std::move(resolved_method_data.first);
RuntimeInterface::SetupResolvedMethod(resolved_method.get());
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
if (ManagedThread::GetCurrent()->GetLanguageContext().GetLanguage() != panda_file::SourceLang::ECMASCRIPT) {
EXPECT_EQ(resolved_method->GetHotnessCounter(), 1U);
}
RuntimeInterface::SetupResolvedMethod(nullptr);
EXPECT_EQ(f->GetAcc().GetLong(), 1);
}
}
void TestVirtualCallExceptions()
{
// Test AbstractMethodError
{
BytecodeEmitter emitter;
emitter.CallVirtRange(0, RuntimeInterface::METHOD_ID.AsIndex());
emitter.Return();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
RuntimeInterface::SetCatchBlockPcOffset(bytecode.size());
emitter.ReturnObj();
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
pandasm::Parser p;
auto source = R"(
.record A {}
.function i32 A.foo(A a0) <noimpl>
)";
auto res = p.Parse(source);
auto class_pf = pandasm::AsmEmitter::Emit(res.Value());
auto class_linker = CreateClassLinker(ManagedThread::GetCurrent());
ASSERT_NE(class_linker, nullptr);
class_linker->AddPandaFile(std::move(class_pf));
PandaString descriptor;
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *object_class = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
Method *callee = object_class->GetMethods().data();
ObjectHeader *obj = AllocObject(object_class);
f->GetVReg(0).SetReference(obj);
RuntimeInterface::SetupResolvedMethod(callee);
RuntimeInterface::SetAbstractMethodErrorData({true, callee});
auto *thread = ManagedThread::GetCurrent();
ObjectHeader *exception = CreateException(thread);
thread->SetException(exception);
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetAbstractMethodErrorData({false, nullptr});
RuntimeInterface::SetupResolvedMethod(nullptr);
ASSERT_FALSE(thread->HasPendingException());
ASSERT_EQ(f->GetAcc().GetReference(), exception);
}
}
TEST_F(InterpreterTest, TestVirtualCallExceptions)
{
TestVirtualCallExceptions();
}
int64_t EntryPoint([[maybe_unused]] Method *method, int64_t a0, int64_t a1)
{
return 100U + a0 + a1;
}
TEST_F(InterpreterTest, TestCallNative)
{
size_t vreg_num = 10;
BytecodeEmitter emitter;
emitter.CallShort(1, 3, RuntimeInterface::METHOD_ID.AsIndex());
emitter.ReturnWide();
std::vector<uint8_t> bytecode;
ASSERT_EQ(emitter.Build(&bytecode), BytecodeEmitter::ErrorCode::SUCCESS);
auto f = CreateFrame(16U, nullptr, nullptr);
InitializeFrame(f.get());
std::vector<int64_t> args1 = {1, 2};
f->GetVReg(1).SetPrimitive(args1[0]);
f->GetVReg(3U).SetPrimitive(args1[1]);
auto cls = CreateClass(panda_file::SourceLang::PANDA_ASSEMBLY);
auto method_data = CreateMethod(cls.get(), f.get(), bytecode);
auto method = std::move(method_data.first);
f->SetMethod(method.get());
std::vector<uint16_t> shorty_buf;
std::vector<uint8_t> method_bytecode;
auto resolved_method_data = CreateResolvedMethod(cls.get(), vreg_num, args1, &method_bytecode, &shorty_buf);
auto resolved_method = std::move(resolved_method_data.first);
RuntimeInterface::SetCompilerHotnessThreshold(1);
resolved_method->SetCompiledEntryPoint(reinterpret_cast<const void *>(EntryPoint));
RuntimeInterface::SetupResolvedMethod(resolved_method.get());
Execute(ManagedThread::GetCurrent(), bytecode.data(), f.get());
RuntimeInterface::SetupResolvedMethod(nullptr);
EXPECT_EQ(f->GetAcc().GetLong(), 103);
}
TEST_F(InterpreterTest, ResolveCtorClass)
{
pandasm::Parser p;
PandaStringStream ss;
ss << R"(
.record R1 {}
.function void R1.ctor(R1 a0) <ctor> {
return.void
}
)";
constexpr size_t METHOD_COUNT = panda_file::MAX_INDEX_16;
for (size_t i = 0; i < METHOD_COUNT; i++) {
ss << ".function void R1.f" << i << "() {" << std::endl;
ss << " call R1.f" << i << std::endl;
ss << " return.void" << std::endl;
ss << "}" << std::endl;
}
ss << R"(
.record R2 {}
.function R1 R2.foo() {
initobj R1.ctor
return.obj
}
)";
auto source = ss.str();
auto res = p.Parse(source.c_str());
ASSERT_TRUE(res) << res.Error().message;
auto pf = pandasm::AsmEmitter::Emit(res.Value());
ASSERT_NE(pf, nullptr) << pandasm::AsmEmitter::GetLastError();
ClassLinker *class_linker = Runtime::GetCurrent()->GetClassLinker();
class_linker->AddPandaFile(std::move(pf));
PandaString descriptor;
auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
Class *klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R2"), &descriptor));
ASSERT_NE(klass, nullptr);
Method *method = klass->GetDirectMethod(utf::CStringAsMutf8("foo"));
ASSERT_NE(method, nullptr);
std::vector<Value> args;
Value v = method->Invoke(ManagedThread::GetCurrent(), args.data());
ASSERT_FALSE(ManagedThread::GetCurrent()->HasPendingException());
auto *ret = v.GetAs<ObjectHeader *>();
ASSERT_NE(ret, nullptr);
ASSERT_EQ(ret->ClassAddr<panda::Class>()->GetName(), "R1");
}
TEST_F(InterpreterTest, ResolveField)
{
pandasm::Parser p;
PandaStringStream ss;
ss << R"(
.record R1 {
i32 f <static>
}
.function void R1.cctor() <cctor> {
ldai 10
ststatic R1.f
return.void
}
.function i32 R1.get() {
ldstatic R1.f
return
}
)";
constexpr size_t METHOD_COUNT = panda_file::MAX_INDEX_16;
for (size_t i = 0; i < METHOD_COUNT; i++) {
ss << ".function void R1.f" << i << "() {" << std::endl;
ss << " call R1.f" << i << std::endl;
ss << " return.void" << std::endl;
ss << "}" << std::endl;
}
ss << R"(
.record R2 {
i32 f <static>
}
.function void R2.cctor() <cctor> {
ldai 20
ststatic R2.f
return.void
}
.function i32 R2.get() {
ldstatic R2.f
return
}
)";
for (size_t i = 0; i < METHOD_COUNT; i++) {
ss << ".function void R2.f" << i << "() {" << std::endl;
ss << " call R2.f" << i << std::endl;
ss << " return.void" << std::endl;
ss << "}" << std::endl;
}
auto source = ss.str();
auto res = p.Parse(source.c_str());
ASSERT_TRUE(res) << res.Error().message;
auto pf = pandasm::AsmEmitter::Emit(res.Value());
ASSERT_NE(pf, nullptr) << pandasm::AsmEmitter::GetLastError();
ClassLinker *class_linker = Runtime::GetCurrent()->GetClassLinker();
class_linker->AddPandaFile(std::move(pf));
auto *extension = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
PandaString descriptor;
{
Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R1"), &descriptor));
ASSERT_NE(klass, nullptr);
Method *method = klass->GetDirectMethod(utf::CStringAsMutf8("get"));
ASSERT_NE(method, nullptr);
std::vector<Value> args;
Value v = method->Invoke(ManagedThread::GetCurrent(), args.data());
ASSERT_FALSE(ManagedThread::GetCurrent()->HasPendingException());
auto ret = v.GetAs<int32_t>();
ASSERT_EQ(ret, 10);
}
{
Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R2"), &descriptor));
ASSERT_NE(klass, nullptr);
Method *method = klass->GetDirectMethod(utf::CStringAsMutf8("get"));
ASSERT_NE(method, nullptr);
std::vector<Value> args;
Value v = method->Invoke(ManagedThread::GetCurrent(), args.data());
ASSERT_FALSE(ManagedThread::GetCurrent()->HasPendingException());
auto ret = v.GetAs<int32_t>();
ASSERT_EQ(ret, 20);
}
}
} // namespace test
} // namespace panda::interpreter