1 /**
2 * Copyright (c) 2021-2024 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 {}
UpdateVmRefs()101 void UpdateVmRefs() override {}
UpdateGlobalObjectStorage()102 void UpdateGlobalObjectStorage() override {}
UpdateClassLinkerContextRoots()103 void UpdateClassLinkerContextRoots() override {}
UpdateThreadLocals()104 void UpdateThreadLocals() override {}
ClearLocalInternalAllocatorPools()105 void ClearLocalInternalAllocatorPools() override {}
106 };
107
108 template <class T>
ToPointer(size_t value)109 static T *ToPointer(size_t value)
110 {
111 return reinterpret_cast<T *>(AlignUp(value, alignof(T)));
112 }
113
114 class RuntimeInterface {
115 public:
116 static constexpr bool NEED_READ_BARRIER = false;
117 static constexpr bool NEED_WRITE_BARRIER = false;
118
119 using InvokeMethodHandler = std::function<Value(ManagedThread *, Method *, Value *)>;
120
121 struct NullPointerExceptionData {
122 bool expected {false};
123 };
124
125 struct ArithmeticException {
126 bool expected {false};
127 };
128
129 struct ArrayIndexOutOfBoundsExceptionData {
130 bool expected {false};
131 coretypes::ArraySsizeT idx {};
132 coretypes::ArraySizeT length {};
133 };
134
135 struct NegativeArraySizeExceptionData {
136 bool expected {false};
137 coretypes::ArraySsizeT size {};
138 };
139
140 struct ClassCastExceptionData {
141 bool expected {false};
142 Class *dstType {};
143 Class *srcType {};
144 };
145
146 struct AbstractMethodError {
147 bool expected {false};
148 Method *method {};
149 };
150
151 struct ArrayStoreExceptionData {
152 bool expected {false};
153 Class *arrayClass {};
154 Class *elemClass {};
155 };
156
157 static constexpr BytecodeId METHOD_ID {0xaabb};
158 static constexpr BytecodeId FIELD_ID {0xeeff};
159 static constexpr BytecodeId TYPE_ID {0x5566};
160 static constexpr BytecodeId LITERALARRAY_ID {0x7788};
161
ResolveLiteralArray(PandaVM * vm,const Method & caller,BytecodeId id)162 static coretypes::Array *ResolveLiteralArray([[maybe_unused]] PandaVM *vm, [[maybe_unused]] const Method &caller,
163 BytecodeId id)
164 {
165 EXPECT_EQ(id, LITERALARRAY_ID);
166 // NOLINTNEXTLINE(readability-magic-numbers)
167 return ToPointer<coretypes::Array>(0x7788);
168 }
169
ResolveMethod(ManagedThread * thread,const Method & caller,BytecodeId id)170 static Method *ResolveMethod([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] const Method &caller,
171 BytecodeId id)
172 {
173 EXPECT_EQ(id, METHOD_ID);
174 return resolvedMethod_;
175 }
176
ResolveField(ManagedThread * thread,const Method & caller,BytecodeId id)177 static Field *ResolveField([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] const Method &caller,
178 BytecodeId id)
179 {
180 EXPECT_EQ(id, FIELD_ID);
181 return resolvedField_;
182 }
183
184 template <bool NEED_INIT>
ResolveClass(ManagedThread * thread,const Method & caller,BytecodeId id)185 static Class *ResolveClass([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] const Method &caller,
186 BytecodeId id)
187 {
188 EXPECT_EQ(id, TYPE_ID);
189 return resolvedClass_;
190 }
191
FindCatchBlock(const Method & method,ObjectHeader * exception,uint32_t pc)192 static uint32_t FindCatchBlock([[maybe_unused]] const Method &method, [[maybe_unused]] ObjectHeader *exception,
193 [[maybe_unused]] uint32_t pc)
194 {
195 return catchBlockPcOffset_;
196 }
197
SetCatchBlockPcOffset(uint32_t pcOffset)198 static void SetCatchBlockPcOffset(uint32_t pcOffset)
199 {
200 catchBlockPcOffset_ = pcOffset;
201 }
202
GetCompilerHotnessThreshold()203 static uint32_t GetCompilerHotnessThreshold()
204 {
205 return jitThreshold_;
206 }
207
IsCompilerEnableJit()208 static bool IsCompilerEnableJit()
209 {
210 return true;
211 }
212
SetCompilerHotnessThreshold(uint32_t threshold)213 static void SetCompilerHotnessThreshold(uint32_t threshold)
214 {
215 jitThreshold_ = threshold;
216 }
217
JITCompileMethod(Method * method)218 static void JITCompileMethod(Method *method)
219 {
220 method->SetCompiledEntryPoint(entryPoint_);
221 }
222
SetCurrentFrame(ManagedThread * thread,Frame * frame)223 static void SetCurrentFrame([[maybe_unused]] ManagedThread *thread, Frame *frame)
224 {
225 ASSERT_NE(frame, nullptr);
226 }
227
GetNotificationManager()228 static RuntimeNotificationManager *GetNotificationManager()
229 {
230 return nullptr;
231 }
232
SetupResolvedMethod(Method * method)233 static void SetupResolvedMethod(Method *method)
234 {
235 ManagedThread::GetCurrent()->GetInterpreterCache()->Clear();
236 resolvedMethod_ = method;
237 }
238
SetupResolvedField(Field * field)239 static void SetupResolvedField(Field *field)
240 {
241 ManagedThread::GetCurrent()->GetInterpreterCache()->Clear();
242 resolvedField_ = field;
243 }
244
SetupResolvedClass(Class * klass)245 static void SetupResolvedClass(Class *klass)
246 {
247 ManagedThread::GetCurrent()->GetInterpreterCache()->Clear();
248 resolvedClass_ = klass;
249 }
250
SetupCatchBlockPcOffset(uint32_t pcOffset)251 static void SetupCatchBlockPcOffset(uint32_t pcOffset)
252 {
253 catchBlockPcOffset_ = pcOffset;
254 }
255
SetupNativeEntryPoint(const void * p)256 static void SetupNativeEntryPoint(const void *p)
257 {
258 entryPoint_ = p;
259 }
260
CreateArray(Class * klass,coretypes::ArraySizeT length)261 static coretypes::Array *CreateArray(Class *klass, coretypes::ArraySizeT length)
262 {
263 EXPECT_EQ(klass, arrayClass_);
264 EXPECT_EQ(length, arrayLength_);
265 return arrayObject_;
266 }
267
SetupArrayClass(Class * klass)268 static void SetupArrayClass(Class *klass)
269 {
270 arrayClass_ = klass;
271 }
272
SetupArrayLength(coretypes::ArraySizeT length)273 static void SetupArrayLength(coretypes::ArraySizeT length)
274 {
275 arrayLength_ = length;
276 }
277
SetupArrayObject(coretypes::Array * obj)278 static void SetupArrayObject(coretypes::Array *obj)
279 {
280 arrayObject_ = obj;
281 }
282
CreateObject(Class * klass)283 static ObjectHeader *CreateObject(Class *klass)
284 {
285 EXPECT_EQ(klass, objectClass_);
286 return object_;
287 }
288
SetupObjectClass(Class * klass)289 static void SetupObjectClass(Class *klass)
290 {
291 objectClass_ = klass;
292 }
293
SetupObject(ObjectHeader * obj)294 static void SetupObject(ObjectHeader *obj)
295 {
296 object_ = obj;
297 }
298
InvokeMethod(ManagedThread * thread,Method * method,Value * args)299 static Value InvokeMethod(ManagedThread *thread, Method *method, Value *args)
300 {
301 return invokeHandler_(thread, method, args);
302 }
303
SetupInvokeMethodHandler(const InvokeMethodHandler & handler)304 static void SetupInvokeMethodHandler(const InvokeMethodHandler &handler)
305 {
306 invokeHandler_ = handler;
307 }
308
309 // Throw exceptions
310
ThrowNullPointerException()311 static void ThrowNullPointerException()
312 {
313 ASSERT_TRUE(npeData_.expected);
314 }
315
ThrowArrayIndexOutOfBoundsException(coretypes::ArraySsizeT idx,coretypes::ArraySizeT length)316 static void ThrowArrayIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySizeT length)
317 {
318 ASSERT_TRUE(arrayOobExceptionData_.expected);
319 ASSERT_EQ(arrayOobExceptionData_.idx, idx);
320 ASSERT_EQ(arrayOobExceptionData_.length, length);
321 }
322
ThrowNegativeArraySizeException(coretypes::ArraySsizeT size)323 static void ThrowNegativeArraySizeException(coretypes::ArraySsizeT size)
324 {
325 ASSERT_TRUE(arrayNegSizeExceptionData_.expected);
326 ASSERT_EQ(arrayNegSizeExceptionData_.size, size);
327 }
328
ThrowArithmeticException()329 static void ThrowArithmeticException()
330 {
331 ASSERT_TRUE(arithmeticExceptionData_.expected);
332 }
333
ThrowClassCastException(Class * dstType,Class * srcType)334 static void ThrowClassCastException(Class *dstType, Class *srcType)
335 {
336 ASSERT_TRUE(classCastExceptionData_.expected);
337 ASSERT_EQ(classCastExceptionData_.dstType, dstType);
338 ASSERT_EQ(classCastExceptionData_.srcType, srcType);
339 }
340
ThrowAbstractMethodError(Method * method)341 static void ThrowAbstractMethodError(Method *method)
342 {
343 ASSERT_TRUE(abstractMethodErrorData_.expected);
344 ASSERT_EQ(abstractMethodErrorData_.method, method);
345 }
346
ThrowIncompatibleClassChangeErrorForMethodConflict(Method * method)347 static void ThrowIncompatibleClassChangeErrorForMethodConflict([[maybe_unused]] Method *method) {}
348
ThrowOutOfMemoryError(const PandaString & msg)349 static void ThrowOutOfMemoryError([[maybe_unused]] const PandaString &msg) {}
350
ThrowVerificationException(const PandaString & msg)351 static void ThrowVerificationException([[maybe_unused]] const PandaString &msg)
352 {
353 // ASSERT_TRUE verification_of_method_exception_data.expected
354 // ASSERT_EQ verification_of_method_exception_data.msg, msg
355 }
356
ThrowArrayStoreException(Class * arrayKlass,Class * elemClass)357 static void ThrowArrayStoreException(Class *arrayKlass, Class *elemClass)
358 {
359 ASSERT_TRUE(arrayStoreExceptionData_.expected);
360 ASSERT_EQ(arrayStoreExceptionData_.arrayClass, arrayKlass);
361 ASSERT_EQ(arrayStoreExceptionData_.elemClass, elemClass);
362 }
363
SetArrayStoreException(ArrayStoreExceptionData data)364 static void SetArrayStoreException(ArrayStoreExceptionData data)
365 {
366 arrayStoreExceptionData_ = data;
367 }
368
SetNullPointerExceptionData(NullPointerExceptionData data)369 static void SetNullPointerExceptionData(NullPointerExceptionData data)
370 {
371 npeData_ = data;
372 }
373
SetArrayIndexOutOfBoundsExceptionData(ArrayIndexOutOfBoundsExceptionData data)374 static void SetArrayIndexOutOfBoundsExceptionData(ArrayIndexOutOfBoundsExceptionData data)
375 {
376 arrayOobExceptionData_ = data;
377 }
378
SetNegativeArraySizeExceptionData(NegativeArraySizeExceptionData data)379 static void SetNegativeArraySizeExceptionData(NegativeArraySizeExceptionData data)
380 {
381 arrayNegSizeExceptionData_ = data;
382 }
383
SetArithmeticExceptionData(ArithmeticException data)384 static void SetArithmeticExceptionData(ArithmeticException data)
385 {
386 arithmeticExceptionData_ = data;
387 }
388
SetClassCastExceptionData(ClassCastExceptionData data)389 static void SetClassCastExceptionData(ClassCastExceptionData data)
390 {
391 classCastExceptionData_ = data;
392 }
393
SetAbstractMethodErrorData(AbstractMethodError data)394 static void SetAbstractMethodErrorData(AbstractMethodError data)
395 {
396 abstractMethodErrorData_ = data;
397 }
398
399 template <bool IS_DYNAMIC = false>
CreateFrame(size_t nregs,Method * method,Frame * prev)400 static Frame *CreateFrame(size_t nregs, Method *method, Frame *prev)
401 {
402 uint32_t extSz = EMPTY_EXT_FRAME_DATA_SIZE;
403 auto allocator = Thread::GetCurrent()->GetVM()->GetHeapManager()->GetInternalAllocator();
404 void *mem = allocator->Allocate(ark::Frame::GetAllocSize(ark::Frame::GetActualSize<IS_DYNAMIC>(nregs), extSz),
405 GetLogAlignment(8), ManagedThread::GetCurrent());
406 return new (Frame::FromExt(mem, extSz)) ark::Frame(mem, method, prev, nregs);
407 }
408
CreateFrameWithActualArgsAndSize(uint32_t size,uint32_t nregs,uint32_t numActualArgs,Method * method,Frame * prev)409 static Frame *CreateFrameWithActualArgsAndSize(uint32_t size, uint32_t nregs, uint32_t numActualArgs,
410 Method *method, Frame *prev)
411 {
412 uint32_t extSz = EMPTY_EXT_FRAME_DATA_SIZE;
413 auto allocator = Thread::GetCurrent()->GetVM()->GetHeapManager()->GetInternalAllocator();
414 void *mem =
415 allocator->Allocate(ark::Frame::GetAllocSize(size, extSz), GetLogAlignment(8), ManagedThread::GetCurrent());
416 if (UNLIKELY(mem == nullptr)) {
417 return nullptr;
418 }
419 return new (Frame::FromExt(mem, extSz)) ark::Frame(mem, method, prev, nregs, numActualArgs);
420 }
421
FreeFrame(ManagedThread * thread,Frame * frame)422 static void FreeFrame(ManagedThread *thread, Frame *frame)
423 {
424 auto allocator = thread->GetVM()->GetHeapManager()->GetInternalAllocator();
425 allocator->Free(frame->GetExt());
426 }
427
GetGC()428 static mem::GC *GetGC()
429 {
430 return &ark::interpreter::test::RuntimeInterface::dummyGc_;
431 }
432
GetMethodName(Method * caller,BytecodeId methodId)433 static const uint8_t *GetMethodName([[maybe_unused]] Method *caller, [[maybe_unused]] BytecodeId methodId)
434 {
435 return nullptr;
436 }
437
GetMethodClass(Method * caller,BytecodeId methodId)438 static Class *GetMethodClass([[maybe_unused]] Method *caller, [[maybe_unused]] BytecodeId methodId)
439 {
440 return resolvedClass_;
441 }
442
GetMethodArgumentsCount(Method * caller,BytecodeId methodId)443 static uint32_t GetMethodArgumentsCount([[maybe_unused]] Method *caller, [[maybe_unused]] BytecodeId methodId)
444 {
445 return 0;
446 }
447
CollectRoots(Frame * frame)448 static void CollectRoots([[maybe_unused]] Frame *frame) {}
449
Safepoint()450 static void Safepoint() {}
451
GetLanguageContext(const Method & method)452 static LanguageContext GetLanguageContext(const Method &method)
453 {
454 return Runtime::GetCurrent()->GetLanguageContext(*method.GetClass());
455 }
456
457 private:
458 static ArrayIndexOutOfBoundsExceptionData arrayOobExceptionData_;
459
460 static NegativeArraySizeExceptionData arrayNegSizeExceptionData_;
461
462 static NullPointerExceptionData npeData_;
463
464 static ArithmeticException arithmeticExceptionData_;
465
466 static ClassCastExceptionData classCastExceptionData_;
467
468 static AbstractMethodError abstractMethodErrorData_;
469
470 static ArrayStoreExceptionData arrayStoreExceptionData_;
471
472 static coretypes::Array *arrayObject_;
473
474 static Class *arrayClass_;
475
476 static coretypes::ArraySizeT arrayLength_;
477
478 static ObjectHeader *object_;
479
480 static Class *objectClass_;
481
482 static Class *resolvedClass_;
483
484 static uint32_t catchBlockPcOffset_;
485
486 static Method *resolvedMethod_;
487
488 static Field *resolvedField_;
489
490 static InvokeMethodHandler invokeHandler_;
491
492 static const void *entryPoint_;
493
494 static uint32_t jitThreshold_;
495
496 static ark::interpreter::test::DummyGC dummyGc_;
497 };
498
499 } // namespace ark::interpreter::test
500
501 #endif // PANDA_RUNTIME_TESTS_INTERPRETER_TEST_RUNTIME_INTERFACE_H_
502