1 /**
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifndef PANDA_RUNTIME_TESTS_INTERPRETER_TEST_RUNTIME_INTERFACE_H_
16 #define PANDA_RUNTIME_TESTS_INTERPRETER_TEST_RUNTIME_INTERFACE_H_
17
18 #include <gtest/gtest.h>
19
20 #include <cstdint>
21
22 #include "libpandafile/file.h"
23 #include "libpandafile/file_items.h"
24 #include "runtime/include/coretypes/array-inl.h"
25 #include "runtime/include/coretypes/string.h"
26 #include "runtime/include/method.h"
27 #include "runtime/include/runtime.h"
28 #include "runtime/include/panda_vm.h"
29 #include "runtime/include/runtime_notification.h"
30 #include "runtime/interpreter/frame.h"
31 #include "runtime/mem/gc/gc.h"
32
33 namespace ark::interpreter::test {
34
35 class DummyGC : public ark::mem::GC {
36 public:
37 NO_COPY_SEMANTIC(DummyGC);
38 NO_MOVE_SEMANTIC(DummyGC);
39
40 explicit DummyGC(ark::mem::ObjectAllocatorBase *objectAllocator, const ark::mem::GCSettings &settings);
41 ~DummyGC() override = default;
42 // NOLINTNEXTLINE(misc-unused-parameters)
WaitForGC(GCTask task)43 bool WaitForGC([[maybe_unused]] GCTask task) override
44 {
45 return false;
46 }
InitGCBits(ark::ObjectHeader * objHeader)47 void InitGCBits([[maybe_unused]] ark::ObjectHeader *objHeader) override {}
InitGCBitsForAllocationInTLAB(ark::ObjectHeader * objHeader)48 void InitGCBitsForAllocationInTLAB([[maybe_unused]] ark::ObjectHeader *objHeader) override {}
Trigger(PandaUniquePtr<GCTask> task)49 bool Trigger([[maybe_unused]] PandaUniquePtr<GCTask> task) override
50 {
51 return false;
52 }
53
IsPinningSupported()54 bool IsPinningSupported() const override
55 {
56 return true;
57 }
58
IsPostponeGCSupported()59 bool IsPostponeGCSupported() const override
60 {
61 return true;
62 }
63
64 private:
VerifyHeap()65 size_t VerifyHeap() override
66 {
67 return 0;
68 }
InitializeImpl()69 void InitializeImpl() override {}
70
PreRunPhasesImpl()71 void PreRunPhasesImpl() override {}
RunPhasesImpl(GCTask & task)72 void RunPhasesImpl([[maybe_unused]] GCTask &task) override {}
IsMarked(const ObjectHeader * object)73 bool IsMarked([[maybe_unused]] const ObjectHeader *object) const override
74 {
75 return false;
76 }
MarkObject(ObjectHeader * object)77 void MarkObject([[maybe_unused]] ObjectHeader *object) override {}
MarkObjectIfNotMarked(ObjectHeader * object)78 bool MarkObjectIfNotMarked([[maybe_unused]] ObjectHeader *object) override
79 {
80 return false;
81 }
MarkReferences(mem::GCMarkingStackType * references,ark::mem::GCPhase gcPhase)82 void MarkReferences([[maybe_unused]] mem::GCMarkingStackType *references,
83 [[maybe_unused]] ark::mem::GCPhase gcPhase) override
84 {
85 }
VisitRoots(const GCRootVisitor & gcRootVisitor,mem::VisitGCRootFlags flags)86 void VisitRoots([[maybe_unused]] const GCRootVisitor &gcRootVisitor,
87 [[maybe_unused]] mem::VisitGCRootFlags flags) override
88 {
89 }
VisitClassRoots(const GCRootVisitor & gcRootVisitor)90 void VisitClassRoots([[maybe_unused]] const GCRootVisitor &gcRootVisitor) override {}
VisitCardTableRoots(mem::CardTable * cardTable,const GCRootVisitor & gcRootVisitor,const MemRangeChecker & rangeChecker,const ObjectChecker & rangeObjectChecker,const ObjectChecker & fromObjectChecker,const uint32_t processedFlag)91 void VisitCardTableRoots([[maybe_unused]] mem::CardTable *cardTable,
92 [[maybe_unused]] const GCRootVisitor &gcRootVisitor,
93 [[maybe_unused]] const MemRangeChecker &rangeChecker,
94 [[maybe_unused]] const ObjectChecker &rangeObjectChecker,
95 [[maybe_unused]] const ObjectChecker &fromObjectChecker,
96 [[maybe_unused]] const uint32_t processedFlag) override
97 {
98 }
CommonUpdateRefsToMovedObjects()99 void CommonUpdateRefsToMovedObjects() override {}
UpdateRefsToMovedObjectsInPygoteSpace()100 void UpdateRefsToMovedObjectsInPygoteSpace() override {}
ClearLocalInternalAllocatorPools()101 void ClearLocalInternalAllocatorPools() override {}
102 };
103
104 template <class T>
ToPointer(size_t value)105 static T *ToPointer(size_t value)
106 {
107 return reinterpret_cast<T *>(AlignUp(value, alignof(T)));
108 }
109
110 class RuntimeInterface {
111 public:
112 static constexpr bool NEED_READ_BARRIER = false;
113 static constexpr bool NEED_WRITE_BARRIER = false;
114
115 using InvokeMethodHandler = std::function<Value(ManagedThread *, Method *, Value *)>;
116
117 struct NullPointerExceptionData {
118 bool expected {false};
119 };
120
121 struct ArithmeticException {
122 bool expected {false};
123 };
124
125 struct ArrayIndexOutOfBoundsExceptionData {
126 bool expected {false};
127 coretypes::ArraySsizeT idx {};
128 coretypes::ArraySizeT length {};
129 };
130
131 struct NegativeArraySizeExceptionData {
132 bool expected {false};
133 coretypes::ArraySsizeT size {};
134 };
135
136 struct ClassCastExceptionData {
137 bool expected {false};
138 Class *dstType {};
139 Class *srcType {};
140 };
141
142 struct AbstractMethodError {
143 bool expected {false};
144 Method *method {};
145 };
146
147 struct ArrayStoreExceptionData {
148 bool expected {false};
149 Class *arrayClass {};
150 Class *elemClass {};
151 };
152
153 static constexpr BytecodeId METHOD_ID {0xaabb};
154 static constexpr BytecodeId FIELD_ID {0xeeff};
155 static constexpr BytecodeId TYPE_ID {0x5566};
156 static constexpr BytecodeId LITERALARRAY_ID {0x7788};
157
ResolveLiteralArray(PandaVM * vm,const Method & caller,BytecodeId id)158 static coretypes::Array *ResolveLiteralArray([[maybe_unused]] PandaVM *vm, [[maybe_unused]] const Method &caller,
159 BytecodeId id)
160 {
161 EXPECT_EQ(id, LITERALARRAY_ID);
162 // NOLINTNEXTLINE(readability-magic-numbers)
163 return ToPointer<coretypes::Array>(0x7788);
164 }
165
ResolveMethod(ManagedThread * thread,const Method & caller,BytecodeId id)166 static Method *ResolveMethod([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] const Method &caller,
167 BytecodeId id)
168 {
169 EXPECT_EQ(id, METHOD_ID);
170 return resolvedMethod_;
171 }
172
ResolveField(ManagedThread * thread,const Method & caller,BytecodeId id,bool isStatic)173 static Field *ResolveField([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] const Method &caller,
174 BytecodeId id, [[maybe_unused]] bool isStatic)
175 {
176 EXPECT_EQ(id, FIELD_ID);
177 return resolvedField_;
178 }
179
180 template <bool NEED_INIT>
ResolveClass(ManagedThread * thread,const Method & caller,BytecodeId id)181 static Class *ResolveClass([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] const Method &caller,
182 BytecodeId id)
183 {
184 EXPECT_EQ(id, TYPE_ID);
185 return resolvedClass_;
186 }
187
FindCatchBlock(const Method & method,ObjectHeader * exception,uint32_t pc)188 static uint32_t FindCatchBlock([[maybe_unused]] const Method &method, [[maybe_unused]] ObjectHeader *exception,
189 [[maybe_unused]] uint32_t pc)
190 {
191 return catchBlockPcOffset_;
192 }
193
SetCatchBlockPcOffset(uint32_t pcOffset)194 static void SetCatchBlockPcOffset(uint32_t pcOffset)
195 {
196 catchBlockPcOffset_ = pcOffset;
197 }
198
GetCompilerHotnessThreshold()199 static uint32_t GetCompilerHotnessThreshold()
200 {
201 return jitThreshold_;
202 }
203
IsCompilerEnableJit()204 static bool IsCompilerEnableJit()
205 {
206 return true;
207 }
208
SetCompilerHotnessThreshold(uint32_t threshold)209 static void SetCompilerHotnessThreshold(uint32_t threshold)
210 {
211 jitThreshold_ = threshold;
212 }
213
JITCompileMethod(Method * method)214 static void JITCompileMethod(Method *method)
215 {
216 method->SetCompiledEntryPoint(entryPoint_);
217 }
218
SetCurrentFrame(ManagedThread * thread,Frame * frame)219 static void SetCurrentFrame([[maybe_unused]] ManagedThread *thread, Frame *frame)
220 {
221 ASSERT_NE(frame, nullptr);
222 }
223
GetNotificationManager()224 static RuntimeNotificationManager *GetNotificationManager()
225 {
226 return nullptr;
227 }
228
SetupResolvedMethod(Method * method)229 static void SetupResolvedMethod(Method *method)
230 {
231 ManagedThread::GetCurrent()->GetInterpreterCache()->Clear();
232 resolvedMethod_ = method;
233 }
234
SetupResolvedField(Field * field)235 static void SetupResolvedField(Field *field)
236 {
237 ManagedThread::GetCurrent()->GetInterpreterCache()->Clear();
238 resolvedField_ = field;
239 }
240
SetupResolvedClass(Class * klass)241 static void SetupResolvedClass(Class *klass)
242 {
243 ManagedThread::GetCurrent()->GetInterpreterCache()->Clear();
244 resolvedClass_ = klass;
245 }
246
SetupCatchBlockPcOffset(uint32_t pcOffset)247 static void SetupCatchBlockPcOffset(uint32_t pcOffset)
248 {
249 catchBlockPcOffset_ = pcOffset;
250 }
251
SetupNativeEntryPoint(const void * p)252 static void SetupNativeEntryPoint(const void *p)
253 {
254 entryPoint_ = p;
255 }
256
CreateArray(Class * klass,coretypes::ArraySizeT length)257 static coretypes::Array *CreateArray(Class *klass, coretypes::ArraySizeT length)
258 {
259 EXPECT_EQ(klass, arrayClass_);
260 EXPECT_EQ(length, arrayLength_);
261 return arrayObject_;
262 }
263
SetupArrayClass(Class * klass)264 static void SetupArrayClass(Class *klass)
265 {
266 arrayClass_ = klass;
267 }
268
SetupArrayLength(coretypes::ArraySizeT length)269 static void SetupArrayLength(coretypes::ArraySizeT length)
270 {
271 arrayLength_ = length;
272 }
273
SetupArrayObject(coretypes::Array * obj)274 static void SetupArrayObject(coretypes::Array *obj)
275 {
276 arrayObject_ = obj;
277 }
278
CreateObject(Class * klass)279 static ObjectHeader *CreateObject(Class *klass)
280 {
281 EXPECT_EQ(klass, objectClass_);
282 return object_;
283 }
284
SetupObjectClass(Class * klass)285 static void SetupObjectClass(Class *klass)
286 {
287 objectClass_ = klass;
288 }
289
SetupObject(ObjectHeader * obj)290 static void SetupObject(ObjectHeader *obj)
291 {
292 object_ = obj;
293 }
294
InvokeMethod(ManagedThread * thread,Method * method,Value * args)295 static Value InvokeMethod(ManagedThread *thread, Method *method, Value *args)
296 {
297 return invokeHandler_(thread, method, args);
298 }
299
SetupInvokeMethodHandler(const InvokeMethodHandler & handler)300 static void SetupInvokeMethodHandler(const InvokeMethodHandler &handler)
301 {
302 invokeHandler_ = handler;
303 }
304
305 // Throw exceptions
306
ThrowNullPointerException()307 static void ThrowNullPointerException()
308 {
309 ASSERT_TRUE(npeData_.expected);
310 }
311
ThrowArrayIndexOutOfBoundsException(coretypes::ArraySsizeT idx,coretypes::ArraySizeT length)312 static void ThrowArrayIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySizeT length)
313 {
314 ASSERT_TRUE(arrayOobExceptionData_.expected);
315 ASSERT_EQ(arrayOobExceptionData_.idx, idx);
316 ASSERT_EQ(arrayOobExceptionData_.length, length);
317 }
318
ThrowNegativeArraySizeException(coretypes::ArraySsizeT size)319 static void ThrowNegativeArraySizeException(coretypes::ArraySsizeT size)
320 {
321 ASSERT_TRUE(arrayNegSizeExceptionData_.expected);
322 ASSERT_EQ(arrayNegSizeExceptionData_.size, size);
323 }
324
ThrowArithmeticException()325 static void ThrowArithmeticException()
326 {
327 ASSERT_TRUE(arithmeticExceptionData_.expected);
328 }
329
ThrowClassCastException(Class * dstType,Class * srcType)330 static void ThrowClassCastException(Class *dstType, Class *srcType)
331 {
332 ASSERT_TRUE(classCastExceptionData_.expected);
333 ASSERT_EQ(classCastExceptionData_.dstType, dstType);
334 ASSERT_EQ(classCastExceptionData_.srcType, srcType);
335 }
336
ThrowAbstractMethodError(Method * method)337 static void ThrowAbstractMethodError(Method *method)
338 {
339 ASSERT_TRUE(abstractMethodErrorData_.expected);
340 ASSERT_EQ(abstractMethodErrorData_.method, method);
341 }
342
ThrowIncompatibleClassChangeErrorForMethodConflict(Method * method)343 static void ThrowIncompatibleClassChangeErrorForMethodConflict([[maybe_unused]] Method *method) {}
344
ThrowOutOfMemoryError(const PandaString & msg)345 static void ThrowOutOfMemoryError([[maybe_unused]] const PandaString &msg) {}
346
ThrowVerificationException(const PandaString & msg)347 static void ThrowVerificationException([[maybe_unused]] const PandaString &msg)
348 {
349 // ASSERT_TRUE verification_of_method_exception_data.expected
350 // ASSERT_EQ verification_of_method_exception_data.msg, msg
351 }
352
ThrowArrayStoreException(Class * arrayKlass,Class * elemClass)353 static void ThrowArrayStoreException(Class *arrayKlass, Class *elemClass)
354 {
355 ASSERT_TRUE(arrayStoreExceptionData_.expected);
356 ASSERT_EQ(arrayStoreExceptionData_.arrayClass, arrayKlass);
357 ASSERT_EQ(arrayStoreExceptionData_.elemClass, elemClass);
358 }
359
SetArrayStoreException(ArrayStoreExceptionData data)360 static void SetArrayStoreException(ArrayStoreExceptionData data)
361 {
362 arrayStoreExceptionData_ = data;
363 }
364
SetNullPointerExceptionData(NullPointerExceptionData data)365 static void SetNullPointerExceptionData(NullPointerExceptionData data)
366 {
367 npeData_ = data;
368 }
369
SetArrayIndexOutOfBoundsExceptionData(ArrayIndexOutOfBoundsExceptionData data)370 static void SetArrayIndexOutOfBoundsExceptionData(ArrayIndexOutOfBoundsExceptionData data)
371 {
372 arrayOobExceptionData_ = data;
373 }
374
SetNegativeArraySizeExceptionData(NegativeArraySizeExceptionData data)375 static void SetNegativeArraySizeExceptionData(NegativeArraySizeExceptionData data)
376 {
377 arrayNegSizeExceptionData_ = data;
378 }
379
SetArithmeticExceptionData(ArithmeticException data)380 static void SetArithmeticExceptionData(ArithmeticException data)
381 {
382 arithmeticExceptionData_ = data;
383 }
384
SetClassCastExceptionData(ClassCastExceptionData data)385 static void SetClassCastExceptionData(ClassCastExceptionData data)
386 {
387 classCastExceptionData_ = data;
388 }
389
SetAbstractMethodErrorData(AbstractMethodError data)390 static void SetAbstractMethodErrorData(AbstractMethodError data)
391 {
392 abstractMethodErrorData_ = data;
393 }
394
395 template <bool IS_DYNAMIC = false>
CreateFrame(size_t nregs,Method * method,Frame * prev)396 static Frame *CreateFrame(size_t nregs, Method *method, Frame *prev)
397 {
398 uint32_t extSz = EMPTY_EXT_FRAME_DATA_SIZE;
399 auto allocator = Thread::GetCurrent()->GetVM()->GetHeapManager()->GetInternalAllocator();
400 void *mem = allocator->Allocate(ark::Frame::GetAllocSize(ark::Frame::GetActualSize<IS_DYNAMIC>(nregs), extSz),
401 GetLogAlignment(8), ManagedThread::GetCurrent());
402 return new (Frame::FromExt(mem, extSz)) ark::Frame(mem, method, prev, nregs);
403 }
404
CreateFrameWithActualArgsAndSize(uint32_t size,uint32_t nregs,uint32_t numActualArgs,Method * method,Frame * prev)405 static Frame *CreateFrameWithActualArgsAndSize(uint32_t size, uint32_t nregs, uint32_t numActualArgs,
406 Method *method, Frame *prev)
407 {
408 uint32_t extSz = EMPTY_EXT_FRAME_DATA_SIZE;
409 auto allocator = Thread::GetCurrent()->GetVM()->GetHeapManager()->GetInternalAllocator();
410 void *mem =
411 allocator->Allocate(ark::Frame::GetAllocSize(size, extSz), GetLogAlignment(8), ManagedThread::GetCurrent());
412 if (UNLIKELY(mem == nullptr)) {
413 return nullptr;
414 }
415 return new (Frame::FromExt(mem, extSz)) ark::Frame(mem, method, prev, nregs, numActualArgs);
416 }
417
FreeFrame(ManagedThread * thread,Frame * frame)418 static void FreeFrame(ManagedThread *thread, Frame *frame)
419 {
420 auto allocator = thread->GetVM()->GetHeapManager()->GetInternalAllocator();
421 allocator->Free(frame->GetExt());
422 }
423
GetGC()424 static mem::GC *GetGC()
425 {
426 return &ark::interpreter::test::RuntimeInterface::dummyGc_;
427 }
428
GetMethodName(Method * caller,BytecodeId methodId)429 static const uint8_t *GetMethodName([[maybe_unused]] Method *caller, [[maybe_unused]] BytecodeId methodId)
430 {
431 return nullptr;
432 }
433
GetMethodClass(Method * caller,BytecodeId methodId)434 static Class *GetMethodClass([[maybe_unused]] Method *caller, [[maybe_unused]] BytecodeId methodId)
435 {
436 return resolvedClass_;
437 }
438
GetMethodArgumentsCount(Method * caller,BytecodeId methodId)439 static uint32_t GetMethodArgumentsCount([[maybe_unused]] Method *caller, [[maybe_unused]] BytecodeId methodId)
440 {
441 return 0;
442 }
443
CollectRoots(Frame * frame)444 static void CollectRoots([[maybe_unused]] Frame *frame) {}
445
Safepoint()446 static void Safepoint() {}
447
GetLanguageContext(const Method & method)448 static LanguageContext GetLanguageContext(const Method &method)
449 {
450 return Runtime::GetCurrent()->GetLanguageContext(*method.GetClass());
451 }
452
453 private:
454 static ArrayIndexOutOfBoundsExceptionData arrayOobExceptionData_;
455
456 static NegativeArraySizeExceptionData arrayNegSizeExceptionData_;
457
458 static NullPointerExceptionData npeData_;
459
460 static ArithmeticException arithmeticExceptionData_;
461
462 static ClassCastExceptionData classCastExceptionData_;
463
464 static AbstractMethodError abstractMethodErrorData_;
465
466 static ArrayStoreExceptionData arrayStoreExceptionData_;
467
468 static coretypes::Array *arrayObject_;
469
470 static Class *arrayClass_;
471
472 static coretypes::ArraySizeT arrayLength_;
473
474 static ObjectHeader *object_;
475
476 static Class *objectClass_;
477
478 static Class *resolvedClass_;
479
480 static uint32_t catchBlockPcOffset_;
481
482 static Method *resolvedMethod_;
483
484 static Field *resolvedField_;
485
486 static InvokeMethodHandler invokeHandler_;
487
488 static const void *entryPoint_;
489
490 static uint32_t jitThreshold_;
491
492 static ark::interpreter::test::DummyGC dummyGc_;
493 };
494
495 } // namespace ark::interpreter::test
496
497 #endif // PANDA_RUNTIME_TESTS_INTERPRETER_TEST_RUNTIME_INTERFACE_H_
498