• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 
16 #ifndef PANDA_RUNTIME_METHOD_INL_H_
17 #define PANDA_RUNTIME_METHOD_INL_H_
18 
19 #include "entrypoints/entrypoints.h"
20 #include "libpandafile/code_data_accessor-inl.h"
21 #include "libpandafile/code_data_accessor.h"
22 #include "libpandafile/file.h"
23 #include "libpandafile/method_data_accessor-inl.h"
24 #include "libpandafile/method_data_accessor.h"
25 #include "libpandafile/proto_data_accessor-inl.h"
26 #include "libpandafile/proto_data_accessor.h"
27 #include "runtime/bridge/bridge.h"
28 #include "runtime/include/class_linker.h"
29 #include "runtime/include/method.h"
30 #include "runtime/include/runtime.h"
31 #include "runtime/include/panda_vm.h"
32 #include "runtime/include/runtime_notification.h"
33 #include "runtime/include/thread-inl.h"
34 #include "runtime/interpreter/interpreter.h"
35 #include "runtime/interpreter/runtime_interface.h"
36 #include "runtime/osr.h"
37 
38 namespace panda {
39 
operator()40 inline void FrameDeleter::operator()(Frame *frame) const
41 {
42     interpreter::RuntimeInterface::FreeFrame(thread_, frame);
43 }
44 
45 class InvokeHelperStatic {
46 public:
47     static constexpr bool is_dynamic = false;
48 
GetFrameSize(uint32_t num_vregs,uint32_t num_declared_args,uint32_t num_actual_args)49     ALWAYS_INLINE inline static uint32_t GetFrameSize(uint32_t num_vregs, uint32_t num_declared_args,
50                                                       [[maybe_unused]] uint32_t num_actual_args)
51     {
52         return num_vregs + num_declared_args;
53     }
54 
InitActualArgs(Frame * frame,Span<Value> args_span,uint32_t num_vregs,uint32_t num_declared_args)55     ALWAYS_INLINE inline static void InitActualArgs(Frame *frame, Span<Value> args_span, uint32_t num_vregs,
56                                                     [[maybe_unused]] uint32_t num_declared_args)
57     {
58         StaticFrameHandler static_frame_helper(frame);
59         uint32_t num_actual_args = args_span.Size();
60         for (size_t i = 0; i < num_actual_args; ++i) {
61             if (args_span[i].IsDecodedTaggedValue()) {
62                 DecodedTaggedValue decoded = args_span[i].GetDecodedTaggedValue();
63                 static_frame_helper.GetVReg(num_vregs + i).SetValue(decoded.value);
64                 static_frame_helper.GetVReg(num_vregs + i).SetTag(decoded.tag);
65             } else if (args_span[i].IsReference()) {
66                 static_frame_helper.GetVReg(num_vregs + i).SetReference(args_span[i].GetAs<ObjectHeader *>());
67             } else {
68                 static_frame_helper.GetVReg(num_vregs + i).SetPrimitive(args_span[i].GetAs<int64_t>());
69             }
70         }
71     }
72 
InterpreterExecute(ManagedThread * thread,const uint8_t * pc,Frame * frame)73     ALWAYS_INLINE inline static void InterpreterExecute(ManagedThread *thread, const uint8_t *pc, Frame *frame)
74     {
75         interpreter::Execute(thread, pc, frame);
76     }
77 
CreateFrame(ManagedThread * thread,uint32_t nregs_size,Method * method,Frame * prev,uint32_t nregs,uint32_t num_actual_args)78     ALWAYS_INLINE static Frame *CreateFrame([[maybe_unused]] ManagedThread *thread, uint32_t nregs_size, Method *method,
79                                             Frame *prev, uint32_t nregs, uint32_t num_actual_args)
80     {
81         return interpreter::RuntimeInterface::CreateFrameWithActualArgsAndSize(nregs_size, nregs, num_actual_args,
82                                                                                method, prev);
83     }
84 };
85 
86 class InvokeHelperDynamic {
87 public:
88     static constexpr bool is_dynamic = true;
89 
GetFrameSize(uint32_t num_vregs,uint32_t num_declared_args,uint32_t num_actual_args)90     ALWAYS_INLINE inline static uint32_t GetFrameSize(uint32_t num_vregs, uint32_t num_declared_args,
91                                                       uint32_t num_actual_args)
92     {
93         return num_vregs + std::max(num_declared_args, num_actual_args);
94     }
95 
InitActualArgs(Frame * frame,Span<coretypes::TaggedValue> args_span,uint32_t num_vregs,uint32_t num_declared_args)96     ALWAYS_INLINE inline static void InitActualArgs(Frame *frame, Span<coretypes::TaggedValue> args_span,
97                                                     uint32_t num_vregs, [[maybe_unused]] uint32_t num_declared_args)
98     {
99         frame->SetDynamic();
100 
101         DynamicFrameHandler dynamic_frame_helper(frame);
102         uint32_t num_actual_args = args_span.Size();
103         for (size_t i = 0; i < num_actual_args; ++i) {
104             dynamic_frame_helper.GetVReg(num_vregs + i).SetValue(args_span[i].GetRawData());
105         }
106 
107         for (size_t i = num_actual_args; i < num_declared_args; i++) {
108             dynamic_frame_helper.GetVReg(num_vregs + i).SetValue(TaggedValue::VALUE_UNDEFINED);
109         }
110     }
111 
InterpreterExecute(ManagedThread * thread,const uint8_t * pc,Frame * frame)112     ALWAYS_INLINE inline static void InterpreterExecute(ManagedThread *thread, const uint8_t *pc, Frame *frame)
113     {
114         interpreter::Execute(thread, pc, frame);
115     }
116 
CompiledCodeExecute(ManagedThread * thread,Method * method,uint32_t num_args,coretypes::TaggedValue * args)117     ALWAYS_INLINE inline static coretypes::TaggedValue CompiledCodeExecute(ManagedThread *thread, Method *method,
118                                                                            uint32_t num_args,
119                                                                            coretypes::TaggedValue *args)
120     {
121         Frame *current_frame = thread->GetCurrentFrame();
122         bool is_compiled = thread->IsCurrentFrameCompiled();
123 
124         ASSERT(num_args >= 2U);  // NOTE(asoldatov): Adjust this check
125         uint64_t ret = InvokeCompiledCodeWithArgArrayDyn(reinterpret_cast<uint64_t *>(args), num_args, current_frame,
126                                                          method, thread);
127         thread->SetCurrentFrameIsCompiled(is_compiled);
128         thread->SetCurrentFrame(current_frame);
129         if (UNLIKELY(thread->HasPendingException())) {
130             return coretypes::TaggedValue::Exception();
131         }
132         return coretypes::TaggedValue(ret);
133     }
134 
CreateFrame(ManagedThread * thread,uint32_t nregs_size,Method * method,Frame * prev,uint32_t nregs,uint32_t num_actual_args)135     ALWAYS_INLINE static Frame *CreateFrame([[maybe_unused]] ManagedThread *thread, uint32_t nregs_size, Method *method,
136                                             Frame *prev, uint32_t nregs, uint32_t num_actual_args)
137     {
138         return interpreter::RuntimeInterface::CreateFrameWithActualArgsAndSize(nregs_size, nregs, num_actual_args,
139                                                                                method, prev);
140     }
141 };
142 
143 template <class InvokeHelper, class ValueT>
GetReturnValueFromException()144 ValueT Method::GetReturnValueFromException()
145 {
146     if constexpr (InvokeHelper::is_dynamic) {  // NOLINT(readability-braces-around-statements)
147         return TaggedValue::Undefined();
148     } else {  // NOLINT(readability-misleading-indentation)
149         if (GetReturnType().IsReference()) {
150             return Value(nullptr);
151         }
152         return Value(static_cast<int64_t>(0));
153     }
154 }
155 
156 template <class InvokeHelper, class ValueT>
GetReturnValueFromAcc(interpreter::AccVRegister & aac_vreg)157 ValueT Method::GetReturnValueFromAcc(interpreter::AccVRegister &aac_vreg)
158 {
159     if constexpr (InvokeHelper::is_dynamic) {  // NOLINT(readability-braces-around-statements)
160         return TaggedValue(aac_vreg.GetAs<uint64_t>());
161     } else {  // NOLINT(readability-misleading-indentation)
162         ASSERT(GetReturnType().GetId() != panda_file::Type::TypeId::TAGGED);
163         if (GetReturnType().GetId() != panda_file::Type::TypeId::VOID) {
164             interpreter::StaticVRegisterRef acc = aac_vreg.AsVRegRef<false>();
165             if (acc.HasObject()) {
166                 return Value(aac_vreg.GetReference());
167             }
168             return Value(aac_vreg.GetLong());
169         }
170         return Value(static_cast<int64_t>(0));
171     }
172 }
173 
InvokeCompiledCode(ManagedThread * thread,uint32_t num_args,Value * args)174 inline Value Method::InvokeCompiledCode(ManagedThread *thread, uint32_t num_args, Value *args)
175 {
176     Frame *current_frame = thread->GetCurrentFrame();
177     Span<Value> args_span(args, num_args);
178     DecodedTaggedValue ret_value {};
179     bool is_compiled = thread->IsCurrentFrameCompiled();
180     // Use frame allocator to alloc memory for parameters as thread can be terminated and
181     // InvokeCompiledCodeWithArgArray will not return in this case we will get memory leak with internal
182     // allocator
183     mem::StackFrameAllocator *allocator = thread->GetStackFrameAllocator();
184     auto values_deleter = [allocator](int64_t *values) {
185         if (values != nullptr) {
186             allocator->Free(values);
187         }
188     };
189     auto values = PandaUniquePtr<int64_t, decltype(values_deleter)>(nullptr, values_deleter);
190     if (num_args > 0) {
191         // In the worse case we are calling a dynamic method in which all arguments are pairs ot int64_t
192         // That is why we allocate 2 x num_actual_args
193         size_t capacity = num_args * sizeof(int64_t);
194         // All allocations though FrameAllocator must be aligned
195         capacity = AlignUp(capacity, GetAlignmentInBytes(DEFAULT_FRAME_ALIGNMENT));
196         values.reset(reinterpret_cast<int64_t *>(allocator->Alloc(capacity)));
197         Span<int64_t> values_span(values.get(), capacity);
198         for (uint32_t i = 0; i < num_args; ++i) {
199             if (args_span[i].IsReference()) {
200                 values_span[i] = reinterpret_cast<int64_t>(args_span[i].GetAs<ObjectHeader *>());
201             } else {
202                 values_span[i] = args_span[i].GetAs<int64_t>();
203             }
204         }
205     }
206 
207     ret_value = InvokeCompiledCodeWithArgArray(values.get(), current_frame, this, thread);
208 
209     thread->SetCurrentFrameIsCompiled(is_compiled);
210     thread->SetCurrentFrame(current_frame);
211     if (UNLIKELY(thread->HasPendingException())) {
212         ret_value = DecodedTaggedValue(0, 0);
213     }
214     return GetReturnValueFromTaggedValue(ret_value);
215 }
216 
217 template <class InvokeHelper, class ValueT>
InvokeInterpretedCode(ManagedThread * thread,uint32_t num_actual_args,ValueT * args)218 ValueT Method::InvokeInterpretedCode(ManagedThread *thread, uint32_t num_actual_args, ValueT *args)
219 {
220     if (!Verify()) {
221         auto ctx = Runtime::GetCurrent()->GetLanguageContext(*this);
222         panda::ThrowVerificationException(ctx, GetFullName());
223         return GetReturnValueFromException<InvokeHelper, ValueT>();
224     }
225 
226     Frame *current_frame = thread->GetCurrentFrame();
227     PandaUniquePtr<Frame, FrameDeleter> frame = InitFrame<InvokeHelper>(thread, num_actual_args, args, current_frame);
228     if (UNLIKELY(frame.get() == nullptr)) {
229         panda::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
230         return GetReturnValueFromException<InvokeHelper, ValueT>();
231     }
232 
233     LOG(DEBUG, INTERPRETER) << "Invoke entry: " << GetFullName();
234     auto is_compiled = thread->IsCurrentFrameCompiled();
235     thread->SetCurrentFrameIsCompiled(false);
236     thread->SetCurrentFrame(frame.get());
237     if (is_compiled && current_frame != nullptr) {
238         // Create C2I bridge frame in case of previous frame is a native frame or other compiler frame.
239         // But create only if the previous frame is not a C2I bridge already.
240         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
241         C2IBridge bridge;
242         if (!StackWalker::IsBoundaryFrame<FrameKind::INTERPRETER>(current_frame)) {
243             bridge = {0, reinterpret_cast<uintptr_t>(current_frame), COMPILED_CODE_TO_INTERPRETER,
244                       thread->GetNativePc()};
245             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
246             frame->SetPrevFrame(reinterpret_cast<Frame *>(&bridge.v_[1]));
247         }
248         // Workaround for
249         // issues #2888 and #2925
250         // We cannot make OSR on the methods called from here, because:
251         // 1. If caller is native method, then C2I bridge, created above, is not complete. It can be fixed by
252         //    allocating full size boundary frame.
253         // 2. If caller is compiled method, then we got here from entrypoint. But currently compiler creates
254         //    boundary frame with pseudo LR value, that doesn't point to the instruction after call, thereby
255         //    OSR will fail. It can be fixed by addresses patching, currently codegen hasn't such machinery.
256         // TODO(msherstennikov): fix issue
257         frame->DisableOsr();
258         Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
259         // interpreter::Execute(thread, GetInstructions(), frame.get());
260         InvokeHelper::InterpreterExecute(thread, GetInstructions(), frame.get());
261         Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
262         thread->SetCurrentFrameIsCompiled(true);
263     } else {
264         Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
265         InvokeHelper::InterpreterExecute(thread, GetInstructions(), frame.get());
266         // interpreter::Execute(thread, GetInstructions(), frame.get());
267         Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
268     }
269     thread->SetCurrentFrame(current_frame);
270 
271     ValueT res = (UNLIKELY(thread->HasPendingException()))
272                      ? GetReturnValueFromException<InvokeHelper, ValueT>()
273                      : GetReturnValueFromAcc<InvokeHelper, ValueT>(frame->GetAcc());
274     LOG(DEBUG, INTERPRETER) << "Invoke exit: " << GetFullName();
275     return res;
276 }
277 
InvokeDyn(ManagedThread * thread,uint32_t num_args,coretypes::TaggedValue * args)278 inline coretypes::TaggedValue Method::InvokeDyn(ManagedThread *thread, uint32_t num_args, coretypes::TaggedValue *args)
279 {
280     return InvokeDyn<InvokeHelperDynamic>(thread, num_args, args);
281 }
282 
283 template <class InvokeHelper>
InvokeDyn(ManagedThread * thread,uint32_t num_args,coretypes::TaggedValue * args)284 inline coretypes::TaggedValue Method::InvokeDyn(ManagedThread *thread, uint32_t num_args, coretypes::TaggedValue *args)
285 {
286     return InvokeImpl<InvokeHelper>(thread, num_args, args, false);
287 }
288 
InvokeContext(ManagedThread * thread,const uint8_t * pc,coretypes::TaggedValue acc,uint32_t nregs,coretypes::TaggedValue * regs)289 inline coretypes::TaggedValue Method::InvokeContext(ManagedThread *thread, const uint8_t *pc,
290                                                     coretypes::TaggedValue acc, uint32_t nregs,
291                                                     coretypes::TaggedValue *regs)
292 {
293     return InvokeContext<InvokeHelperDynamic>(thread, pc, acc, nregs, regs);
294 }
295 
296 template <class InvokeHelper>
InvokeContext(ManagedThread * thread,const uint8_t * pc,coretypes::TaggedValue acc,uint32_t nregs,coretypes::TaggedValue * regs)297 inline coretypes::TaggedValue Method::InvokeContext(ManagedThread *thread, const uint8_t *pc,
298                                                     coretypes::TaggedValue acc, uint32_t nregs,
299                                                     coretypes::TaggedValue *regs)
300 {
301     static_assert(InvokeHelper::is_dynamic == true);
302     ASSERT(GetReturnType().GetId() == panda_file::Type::TypeId::VOID ||
303            GetReturnType().GetId() == panda_file::Type::TypeId::TAGGED);
304 
305     TaggedValue res(TaggedValue::VALUE_UNDEFINED);
306 
307     if (!Verify()) {
308         auto ctx = Runtime::GetCurrent()->GetLanguageContext(*this);
309         panda::ThrowVerificationException(ctx, GetFullName());
310         return res;
311     }
312 
313     Frame *current_frame = thread->GetCurrentFrame();
314     PandaUniquePtr<Frame, FrameDeleter> frame(
315         interpreter::RuntimeInterface::CreateFrameWithActualArgs<true>(nregs, nregs, this,
316                                                                                       current_frame),
317         FrameDeleter(thread));
318     if (UNLIKELY(frame.get() == nullptr)) {
319         panda::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
320         return res;
321     }
322 
323     frame->SetDynamic();
324 
325     DynamicFrameHandler dynamic_frame_helper(frame.get());
326     Span<TaggedValue> args_span(regs, nregs);
327     for (size_t i = 0; i < nregs; ++i) {
328         dynamic_frame_helper.GetVReg(i).SetValue(args_span[i].GetRawData());
329     }
330 
331     LOG(DEBUG, INTERPRETER) << "Invoke entry: " << GetFullName();
332 
333     dynamic_frame_helper.GetAcc().SetValue(acc.GetRawData());
334     thread->SetCurrentFrame(frame.get());
335 
336     Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
337     InvokeHelper::InterpreterExecute(thread, pc, frame.get());
338     Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
339 
340     thread->SetCurrentFrame(current_frame);
341     res = TaggedValue(dynamic_frame_helper.GetAcc().GetAs<uint64_t>());
342 
343     LOG(DEBUG, INTERPRETER) << "Invoke exit: " << GetFullName();
344     return res;
345 }
346 
347 template <class InvokeHelper, class ValueT>
EnterNativeMethodFrame(ManagedThread * thread,uint32_t num_vregs,uint32_t num_args,ValueT * args)348 Frame *Method::EnterNativeMethodFrame(ManagedThread *thread, uint32_t num_vregs, uint32_t num_args, ValueT *args)
349 {
350     Frame *current_frame = thread->GetCurrentFrame();
351     PandaUniquePtr<Frame, FrameDeleter> frame =
352         InitFrameWithNumVRegs<InvokeHelper, ValueT, true>(thread, num_vregs, num_args, args, current_frame);
353     if (UNLIKELY(frame.get() == nullptr)) {
354         panda::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
355         return nullptr;
356     }
357 
358     LOG(DEBUG, INTERPRETER) << "Enter native frame";
359 
360     thread->SetCurrentFrame(frame.get());
361     return frame.release();
362 }
363 
ExitNativeMethodFrame(ManagedThread * thread)364 inline void Method::ExitNativeMethodFrame(ManagedThread *thread)
365 {
366     Frame *current_frame = thread->GetCurrentFrame();
367     ASSERT(current_frame != nullptr);
368 
369     LOG(DEBUG, INTERPRETER) << "Exit native frame";
370 
371     thread->SetCurrentFrame(current_frame->GetPrevFrame());
372     FreeFrame(current_frame);
373 }
374 
375 template <class InvokeHelper, class ValueT>
InitFrame(ManagedThread * thread,uint32_t num_actual_args,ValueT * args,Frame * current_frame)376 PandaUniquePtr<Frame, FrameDeleter> Method::InitFrame(ManagedThread *thread, uint32_t num_actual_args, ValueT *args,
377                                                       Frame *current_frame)
378 {
379     ASSERT(code_id_.IsValid());
380     auto num_vregs = panda_file::CodeDataAccessor::GetNumVregs(*(panda_file_), code_id_);
381     return InitFrameWithNumVRegs<InvokeHelper, ValueT, false>(thread, num_vregs, num_actual_args, args, current_frame);
382 }
383 
384 template <class InvokeHelper, class ValueT, bool is_native_method>
InitFrameWithNumVRegs(ManagedThread * thread,uint32_t num_vregs,uint32_t num_actual_args,ValueT * args,Frame * current_frame)385 PandaUniquePtr<Frame, FrameDeleter> Method::InitFrameWithNumVRegs(ManagedThread *thread, uint32_t num_vregs,
386                                                                   uint32_t num_actual_args, ValueT *args,
387                                                                   Frame *current_frame)
388 {
389     Span<ValueT> args_span(args, num_actual_args);
390 
391     uint32_t num_declared_args = GetNumArgs();
392     uint32_t frame_size = InvokeHelper::GetFrameSize(num_vregs, num_declared_args, num_actual_args);
393 
394     Frame *frame_ptr;
395     // NOLINTNEXTLINE(readability-braces-around-statements)
396     if constexpr (is_native_method) {
397         frame_ptr = interpreter::RuntimeInterface::CreateNativeFrameWithActualArgs<InvokeHelper::is_dynamic>(
398             frame_size, num_actual_args, this, current_frame);
399     } else {  // NOLINTNEXTLINE(readability-braces-around-statements)
400         frame_ptr = InvokeHelper::CreateFrame(thread, Frame::GetActualSize<InvokeHelper::is_dynamic>(frame_size), this,
401                                               current_frame, frame_size, num_actual_args);
402     }
403     PandaUniquePtr<Frame, FrameDeleter> frame(frame_ptr, FrameDeleter(thread));
404     if (UNLIKELY(frame.get() == nullptr)) {
405         return frame;
406     }
407 
408     InvokeHelper::InitActualArgs(frame.get(), args_span, num_vregs, num_declared_args);
409 
410     frame->SetInvoke();
411     return frame;
412 }
413 
414 template <class InvokeHelper, class ValueT>
InvokeImpl(ManagedThread * thread,uint32_t num_actual_args,ValueT * args,bool proxy_call)415 ValueT Method::InvokeImpl(ManagedThread *thread, uint32_t num_actual_args, ValueT *args, bool proxy_call)
416 {
417     IncrementHotnessCounter(0, nullptr);
418 
419     // Currently, proxy methods should always be invoked in the interpreter. This constraint should be relaxed once
420     // we support same frame layout for interpreter and compiled methods.
421     // TODO(msherstennikov): remove `proxy_call`
422     bool run_interpreter = !HasCompiledCode() || proxy_call;
423     ASSERT(!(proxy_call && IsNative()));
424     if (!run_interpreter) {
425         if constexpr (InvokeHelper::is_dynamic) {  // NOLINT(readability-braces-around-statements)
426             return InvokeHelper::CompiledCodeExecute(thread, this, num_actual_args, args);
427         } else {  // NOLINT(readability-misleading-indentation)
428             return InvokeCompiledCode(thread, num_actual_args, args);
429         }
430     }
431     if (!thread->template StackOverflowCheck<true, false>()) {
432         return GetReturnValueFromException<InvokeHelper, ValueT>();
433     }
434 
435     return InvokeInterpretedCode<InvokeHelper>(thread, num_actual_args, args);
436 }
437 
438 template <class AccVRegisterPtrT>
SetAcc(AccVRegisterPtrT acc)439 inline void Method::SetAcc([[maybe_unused]] AccVRegisterPtrT acc)
440 {
441     if constexpr (!std::is_same_v<AccVRegisterPtrT, std::nullptr_t>) {  // NOLINT
442         if (acc != nullptr) {
443             ManagedThread::GetCurrent()->GetCurrentFrame()->SetAcc(*acc);
444         }
445     }
446 }
447 
448 /**
449  * Increment method's hotness counter.
450  * @param bytecode_offset Offset of the target bytecode instruction. Used only for OSR.
451  * @param acc Pointer to the accumulator, it is needed because interpreter uses own Frame, not the one in the method.
452  *            Used only for OSR.
453  * @return true if OSR has been occurred
454  */
455 template <class AccVRegisterPtrT>
IncrementHotnessCounter(uintptr_t bytecode_offset,AccVRegisterPtrT acc,bool osr)456 inline bool Method::IncrementHotnessCounter(uintptr_t bytecode_offset, [[maybe_unused]] AccVRegisterPtrT acc, bool osr)
457 {
458     bool is_jit_enabled = Runtime::GetCurrent()->IsJitEnabled();
459 
460     if (!is_jit_enabled) {
461         // hotness_counter_ may be used as profiling information so it should be incremented even with disabled JIT:
462         stor_16_pair_.hotness_counter_++;
463         return false;
464     }
465     // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
466     if constexpr (!ArchTraits<RUNTIME_ARCH>::SUPPORT_OSR) {
467         ASSERT(!osr);
468     }
469 
470     auto runtime = Runtime::GetCurrent();
471     auto &options = Runtime::GetOptions();
472     uint32_t threshold = options.GetCompilerHotnessThreshold();
473     if (UNLIKELY(GetHotnessCounter() >= threshold)) {
474         CompilationStage status = GetCompilationStatus();
475         if (!(status == FAILED || status == WAITING || status == COMPILATION)) {
476             ASSERT((!osr) == (acc == nullptr));
477             SetAcc<AccVRegisterPtrT>(acc);
478 
479             return runtime->GetPandaVM()->GetCompiler()->CompileMethod(this, bytecode_offset, osr);
480         }
481         if (status == WAITING) {
482             IncrementHotnessCounter();
483         }
484     } else {
485         if (GetHotnessCounter() == options.GetCompilerProfilingThreshold() && !HasCompiledCode() && !IsProfiling()) {
486             StartProfiling();
487         }
488         IncrementHotnessCounter();
489     }
490     return false;
491 }
492 
493 template <typename Callback>
EnumerateTypes(Callback handler)494 void Method::EnumerateTypes(Callback handler) const
495 {
496     panda_file::MethodDataAccessor mda(*(panda_file_), file_id_);
497     mda.EnumerateTypesInProto(handler);
498 }
499 
500 template <typename Callback>
EnumerateTryBlocks(Callback callback)501 void Method::EnumerateTryBlocks(Callback callback) const
502 {
503     ASSERT(!IsAbstract());
504 
505     panda_file::MethodDataAccessor mda(*(panda_file_), file_id_);
506     panda_file::CodeDataAccessor cda(*(panda_file_), mda.GetCodeId().value());
507 
508     cda.EnumerateTryBlocks(callback);
509 }
510 
511 template <typename Callback>
EnumerateCatchBlocks(Callback callback)512 void Method::EnumerateCatchBlocks(Callback callback) const
513 {
514     ASSERT(!IsAbstract());
515 
516     using TryBlock = panda_file::CodeDataAccessor::TryBlock;
517     using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
518 
519     EnumerateTryBlocks([&callback, code = GetInstructions()](const TryBlock &try_block) {
520         bool next = true;
521         const uint8_t *try_start_pc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(code) +
522                                                                   static_cast<uintptr_t>(try_block.GetStartPc()));
523         const uint8_t *try_end_pc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(try_start_pc) +
524                                                                 static_cast<uintptr_t>(try_block.GetLength()));
525         // ugly, but API of TryBlock is bad designed: enumaration is paired with mutation & updating
526         const_cast<TryBlock &>(try_block).EnumerateCatchBlocks(
527             [&callback, &next, try_start_pc, try_end_pc](const CatchBlock &catch_block) {
528                 return next = callback(try_start_pc, try_end_pc, catch_block);
529             });
530         return next;
531     });
532 }
533 
534 template <typename Callback>
EnumerateExceptionHandlers(Callback callback)535 void Method::EnumerateExceptionHandlers(Callback callback) const
536 {
537     ASSERT(!IsAbstract());
538 
539     using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
540 
541     EnumerateCatchBlocks([this, callback = std::move(callback)](const uint8_t *try_start_pc, const uint8_t *try_end_pc,
542                                                                 const CatchBlock &catch_block) {
543         auto type_idx = catch_block.GetTypeIdx();
544         const uint8_t *pc =
545             &GetInstructions()[catch_block.GetHandlerPc()];  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
546         size_t size = catch_block.GetCodeSize();
547         const Class *cls = nullptr;
548         if (type_idx != panda_file::INVALID_INDEX) {
549             Runtime *runtime = Runtime::GetCurrent();
550             auto type_id = GetClass()->ResolveClassIndex(type_idx);
551             // todo: remove next code, after solving #1220 '[Runtime] Proposal for class descriptors in panda files'
552             //       and clean up of ClassLinker API
553             // cut
554             LanguageContext ctx = runtime->GetLanguageContext(*this);
555             cls = runtime->GetClassLinker()->GetExtension(ctx)->GetClass(*(panda_file_), type_id);
556             // end cut
557         }
558         return callback(try_start_pc, try_end_pc, cls, pc, size);
559     });
560 }
561 
562 }  // namespace panda
563 
564 #endif  // !PANDA_RUNTIME_METHOD_INL_H_
565