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 
16 #include "runtime/include/exceptions.h"
17 
18 #include <libpandabase/utils/cframe_layout.h>
19 
20 #include "runtime/bridge/bridge.h"
21 #include "runtime/deoptimization.h"
22 #include "runtime/entrypoints/entrypoints.h"
23 #include "runtime/include/coretypes/string.h"
24 #include "runtime/include/object_header-inl.h"
25 #include "runtime/include/runtime.h"
26 #include "runtime/include/stack_walker.h"
27 #include "runtime/mem/vm_handle.h"
28 #include "libpandabase/utils/asan_interface.h"
29 #include "libpandabase/utils/logger.h"
30 #include "libpandabase/utils/utf.h"
31 #include "libpandafile/class_data_accessor-inl.h"
32 #include "libpandafile/method_data_accessor-inl.h"
33 #include "events/events.h"
34 #include "runtime/handle_base-inl.h"
35 
36 namespace ark {
37 
ThrowException(const LanguageContext &ctx, ManagedThread *thread, const uint8_t *mutf8Name, const uint8_t *mutf8Msg)38 void ThrowException(const LanguageContext &ctx, ManagedThread *thread, const uint8_t *mutf8Name,
39                     const uint8_t *mutf8Msg)
40 {
41     ctx.ThrowException(thread, mutf8Name, mutf8Msg);
42 }
43 
GetLanguageContext(ManagedThread *thread)44 static LanguageContext GetLanguageContext(ManagedThread *thread)
45 {
46     ASSERT(thread != nullptr);
47     return thread->GetVM()->GetLanguageContext();
48 }
49 
ThrowNullPointerException()50 void ThrowNullPointerException()
51 {
52     auto *thread = ManagedThread::GetCurrent();
53     auto ctx = GetLanguageContext(thread);
54     ThrowNullPointerException(ctx, thread);
55 }
56 
ThrowNullPointerException(const LanguageContext &ctx, ManagedThread *thread)57 void ThrowNullPointerException(const LanguageContext &ctx, ManagedThread *thread)
58 {
59     ThrowException(ctx, thread, ctx.GetNullPointerExceptionClassDescriptor(), nullptr);
60     SetExceptionEvent(events::ExceptionType::NULL_CHECK, thread);
61 }
62 
ThrowStackOverflowException(ManagedThread *thread)63 void ThrowStackOverflowException(ManagedThread *thread)
64 {
65     auto ctx = GetLanguageContext(thread);
66     ctx.ThrowStackOverflowException(thread);
67 }
68 
ThrowArrayIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySizeT length)69 void ThrowArrayIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySizeT length)
70 {
71     auto *thread = ManagedThread::GetCurrent();
72     auto ctx = GetLanguageContext(thread);
73     ThrowArrayIndexOutOfBoundsException(idx, length, ctx, thread);
74 }
75 
ThrowArrayIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySizeT length, const LanguageContext &ctx, ManagedThread *thread)76 void ThrowArrayIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySizeT length,
77                                          const LanguageContext &ctx, ManagedThread *thread)
78 {
79     PandaString msg {"idx = " + ToPandaString(idx) + "; length = " + ToPandaString(length)};
80     ThrowException(ctx, thread, ctx.GetArrayIndexOutOfBoundsExceptionClassDescriptor(),
81                    utf::CStringAsMutf8(msg.c_str()));
82     SetExceptionEvent(events::ExceptionType::BOUND_CHECK, thread);
83 }
84 
ThrowIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySsizeT length)85 void ThrowIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySsizeT length)
86 {
87     auto *thread = ManagedThread::GetCurrent();
88     auto ctx = GetLanguageContext(thread);
89     PandaString msg {"idx = " + ToPandaString(idx) + "; length = " + ToPandaString(length)};
90     ThrowException(ctx, thread, ctx.GetIndexOutOfBoundsExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
91 }
92 
ThrowIllegalStateException(const PandaString &msg)93 void ThrowIllegalStateException(const PandaString &msg)
94 {
95     auto *thread = ManagedThread::GetCurrent();
96     auto ctx = GetLanguageContext(thread);
97     ThrowException(ctx, thread, ctx.GetIllegalStateExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
98 }
99 
ThrowStringIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySizeT length)100 void ThrowStringIndexOutOfBoundsException(coretypes::ArraySsizeT idx, coretypes::ArraySizeT length)
101 {
102     auto *thread = ManagedThread::GetCurrent();
103     auto ctx = GetLanguageContext(thread);
104     PandaString msg {"idx = " + ToPandaString(idx) + "; length = " + ToPandaString(length)};
105     ThrowException(ctx, thread, ctx.GetStringIndexOutOfBoundsExceptionClassDescriptor(),
106                    utf::CStringAsMutf8(msg.c_str()));
107     SetExceptionEvent(events::ExceptionType::BOUND_CHECK, thread);
108 }
109 
ThrowNegativeArraySizeException(coretypes::ArraySsizeT size)110 void ThrowNegativeArraySizeException(coretypes::ArraySsizeT size)
111 {
112     auto *thread = ManagedThread::GetCurrent();
113     auto ctx = GetLanguageContext(thread);
114     PandaString msg {"size = " + ToPandaString(size)};
115     ThrowException(ctx, thread, ctx.GetNegativeArraySizeExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
116 }
117 
ThrowNegativeArraySizeException(const PandaString &msg)118 void ThrowNegativeArraySizeException(const PandaString &msg)
119 {
120     auto *thread = ManagedThread::GetCurrent();
121     auto ctx = GetLanguageContext(thread);
122     ThrowException(ctx, thread, ctx.GetNegativeArraySizeExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
123     SetExceptionEvent(events::ExceptionType::NEGATIVE_SIZE, thread);
124 }
125 
ThrowArithmeticException()126 void ThrowArithmeticException()
127 {
128     auto *thread = ManagedThread::GetCurrent();
129     auto ctx = GetLanguageContext(thread);
130     ThrowException(ctx, thread, ctx.GetArithmeticExceptionClassDescriptor(), utf::CStringAsMutf8("/ by zero"));
131     SetExceptionEvent(events::ExceptionType::ARITHMETIC, thread);
132 }
133 
ThrowClassCastException(const Class *dstType, const Class *srcType)134 void ThrowClassCastException(const Class *dstType, const Class *srcType)
135 {
136     auto *thread = ManagedThread::GetCurrent();
137     auto ctx = GetLanguageContext(thread);
138     PandaString msg {srcType->GetName() + " cannot be cast to " + dstType->GetName()};
139     ThrowException(ctx, thread, ctx.GetClassCastExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
140     SetExceptionEvent(events::ExceptionType::CAST_CHECK, thread);
141 }
142 
ThrowAbstractMethodError(const Method *method)143 void ThrowAbstractMethodError(const Method *method)
144 {
145     auto *thread = ManagedThread::GetCurrent();
146     auto ctx = GetLanguageContext(thread);
147     PandaString msg {"abstract method \"" + method->GetClass()->GetName() + "."};
148     msg += utf::Mutf8AsCString(method->GetName().data);
149     msg += "\"";
150     ThrowException(ctx, thread, ctx.GetAbstractMethodErrorClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
151     SetExceptionEvent(events::ExceptionType::ABSTRACT_METHOD, thread);
152 }
153 
ThrowIncompatibleClassChangeErrorForMethodConflict(const Method *method)154 void ThrowIncompatibleClassChangeErrorForMethodConflict(const Method *method)
155 {
156     auto *thread = ManagedThread::GetCurrent();
157     auto ctx = GetLanguageContext(thread);
158     PandaString msg {"Conflicting default method implementations \"" + method->GetClass()->GetName() + "."};
159     msg += utf::Mutf8AsCString(method->GetName().data);
160     msg += "\"";
161     ThrowException(ctx, thread, ctx.GetIncompatibleClassChangeErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
162     SetExceptionEvent(events::ExceptionType::ICCE_METHOD_CONFLICT, thread);
163 }
164 
ThrowArrayStoreException(const Class *arrayClass, const Class *elementClass)165 void ThrowArrayStoreException(const Class *arrayClass, const Class *elementClass)
166 {
167     PandaStringStream ss;
168     ss << elementClass->GetName() << " cannot be stored in an array of type " << arrayClass->GetName();
169     ThrowArrayStoreException(ss.str());
170 }
171 
ThrowArrayStoreException(const PandaString &msg)172 void ThrowArrayStoreException(const PandaString &msg)
173 {
174     auto *thread = ManagedThread::GetCurrent();
175     auto ctx = GetLanguageContext(thread);
176 
177     ThrowException(ctx, thread, ctx.GetArrayStoreExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
178 }
179 
ThrowRuntimeException(const PandaString &msg)180 void ThrowRuntimeException(const PandaString &msg)
181 {
182     auto *thread = ManagedThread::GetCurrent();
183     auto ctx = GetLanguageContext(thread);
184 
185     ThrowException(ctx, thread, ctx.GetRuntimeExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
186 }
187 
ThrowIllegalArgumentException(const PandaString &msg)188 void ThrowIllegalArgumentException(const PandaString &msg)
189 {
190     auto *thread = ManagedThread::GetCurrent();
191     auto ctx = GetLanguageContext(thread);
192 
193     ThrowException(ctx, thread, ctx.GetIllegalArgumentExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
194 }
195 
ThrowClassCircularityError(const PandaString &className, const LanguageContext &ctx)196 void ThrowClassCircularityError(const PandaString &className, const LanguageContext &ctx)
197 {
198     auto *thread = ManagedThread::GetCurrent();
199     PandaString msg = "Class or interface \"" + className + "\" is its own superclass or superinterface";
200     ThrowException(ctx, thread, ctx.GetClassCircularityErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
201 }
202 
DropCFrameIfNecessary(ManagedThread *thread, StackWalker *stack, Frame *origFrame, FrameAccessor nextFrame, Method *method)203 NO_ADDRESS_SANITIZE void DropCFrameIfNecessary(ManagedThread *thread, StackWalker *stack, Frame *origFrame,
204                                                FrameAccessor nextFrame, Method *method)
205 {
206     if (!nextFrame.IsCFrame()) {
207         if (origFrame != nullptr) {
208             FreeFrame(origFrame);
209         }
210         thread->SetCurrentFrame(nextFrame.GetIFrame());
211         LOG(DEBUG, INTEROP) << "DropCompiledFrameAndReturn. Next frame isn't CFrame";
212         DropCompiledFrame(stack);
213         UNREACHABLE();
214     }
215 
216     if (nextFrame.GetCFrame().IsNative()) {
217         if (origFrame != nullptr) {
218             FreeFrame(origFrame);
219         }
220         LOG(DEBUG, INTEROP) << "DropCompiledFrameAndReturn. Next frame is NATIVE";
221         DropCompiledFrame(stack);
222         UNREACHABLE();
223     }
224 
225     if (method->IsStaticConstructor()) {
226         if (origFrame != nullptr) {
227             FreeFrame(origFrame);
228         }
229         LOG(DEBUG, INTEROP) << "DropCompiledFrameAndReturn. Next frame is StaticConstructor";
230         DropCompiledFrame(stack);
231         UNREACHABLE();
232     }
233 }
234 
235 /**
236  * The function finds the corresponding catch block for the exception in the thread.
237  * The function uses thread as an exception storage because:
238  *  1. thread's exception is a GC root
239  *  2. we cannot use HandleScope here because the function is [[noreturn]]
240  */
241 // NOLINTNEXTLINE(google-runtime-references)
FindCatchBlockInCFrames(ManagedThread *thread, StackWalker *stack, Frame *origFrame)242 NO_ADDRESS_SANITIZE void FindCatchBlockInCFrames(ManagedThread *thread, StackWalker *stack, Frame *origFrame)
243 {
244     auto nextFrame = stack->GetNextFrame();
245     for (; stack->HasFrame(); stack->NextFrame(), nextFrame = stack->GetNextFrame()) {
246         LOG(DEBUG, INTEROP) << "Search for the catch block in " << stack->GetMethod()->GetFullName();
247 
248         auto pc = stack->GetBytecodePc();
249         auto *method = stack->GetMethod();
250         ASSERT(method != nullptr);
251         uint32_t pcOffset = method->FindCatchBlock(thread->GetException()->ClassAddr<Class>(), pc);
252         if (pcOffset != panda_file::INVALID_OFFSET) {
253             if (origFrame != nullptr) {
254                 FreeFrame(origFrame);
255             }
256 
257             LOG(DEBUG, INTEROP) << "Catch block is found in " << stack->GetMethod()->GetFullName();
258             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
259             Deoptimize(stack, method->GetInstructions() + pcOffset);
260             UNREACHABLE();
261         }
262 
263         thread->GetVM()->HandleReturnFrame();
264 
265         DropCFrameIfNecessary(thread, stack, origFrame, nextFrame, method);
266 
267         if (stack->IsInlined()) {
268             continue;
269         }
270 
271         auto prev = stack->GetCFrame().GetPrevFrame();
272         if (stack->GetBoundaryFrameMethod<FrameKind::COMPILER>(prev) == FrameBridgeKind::BYPASS) {
273             /**
274              * There is bypass bridge and current frame is not inlined, hence we are going to exit compiled
275              * function. Dynamic languages may do c2c call through runtime, so it's necessary to return to exit
276              * active function properly.
277              */
278             if (origFrame != nullptr) {
279                 FreeFrame(origFrame);
280             }
281             LOG(DEBUG, INTEROP) << "DropCompiledFrameAndReturn. Next frame is caller's cframe";
282             DropCompiledFrame(stack);
283             UNREACHABLE();
284         }
285     }
286 
287     if (nextFrame.IsValid()) {
288         LOG(DEBUG, INTEROP) << "Exception " << thread->GetException()->ClassAddr<Class>()->GetName() << " isn't found";
289         EVENT_METHOD_EXIT(stack->GetMethod()->GetFullName(), events::MethodExitKind::COMPILED,
290                           thread->RecordMethodExit());
291         thread->GetVM()->HandleReturnFrame();
292         DropCompiledFrame(stack);
293     }
294     UNREACHABLE();
295 }
296 
297 /**
298  * The function finds the corresponding catch block for the exception in the thread.
299  * The function uses thread as an exception storage because:
300  *  1. thread's exception is a GC root
301  *  2. we cannot use Handl;eScope her ebecause the function is [[noreturn]]
302  */
FindCatchBlockInCallStack(ManagedThread *thread)303 NO_ADDRESS_SANITIZE void FindCatchBlockInCallStack(ManagedThread *thread)
304 {
305     auto stack = StackWalker::Create(thread);
306     auto origFrame = stack.GetIFrame();
307     ASSERT(!stack.IsCFrame());
308     LOG(DEBUG, INTEROP) << "Enter in FindCatchBlockInCallStack for " << origFrame->GetMethod()->GetFullName();
309     // Exception will be handled in the Method::Invoke's caller
310     if (origFrame->IsInvoke()) {
311         return;
312     }
313 
314     stack.NextFrame();
315 
316     // NATIVE frames can handle exceptions as well
317     if (!stack.HasFrame() || !stack.IsCFrame() || stack.GetCFrame().IsNative()) {
318         return;
319     }
320     thread->GetVM()->HandleReturnFrame();
321     FindCatchBlockInCFrames(thread, &stack, origFrame);
322 }
323 
ThrowFileNotFoundException(const PandaString &msg)324 void ThrowFileNotFoundException(const PandaString &msg)
325 {
326     auto *thread = ManagedThread::GetCurrent();
327     auto ctx = GetLanguageContext(thread);
328 
329     ThrowException(ctx, thread, ctx.GetFileNotFoundExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
330 }
331 
ThrowIOException(const PandaString &msg)332 void ThrowIOException(const PandaString &msg)
333 {
334     auto *thread = ManagedThread::GetCurrent();
335     auto ctx = GetLanguageContext(thread);
336 
337     ThrowException(ctx, thread, ctx.GetIOExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
338 }
339 
ThrowIllegalAccessException(const PandaString &msg)340 void ThrowIllegalAccessException(const PandaString &msg)
341 {
342     auto *thread = ManagedThread::GetCurrent();
343     auto ctx = GetLanguageContext(thread);
344 
345     ThrowException(ctx, thread, ctx.GetIllegalAccessExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
346 }
347 
ThrowOutOfMemoryError(ManagedThread *thread, const PandaString &msg)348 void ThrowOutOfMemoryError(ManagedThread *thread, const PandaString &msg)
349 {
350     auto ctx = GetLanguageContext(thread);
351 
352     if (thread->IsThrowingOOM()) {
353         thread->SetUsePreAllocObj(true);
354     }
355 
356     thread->SetThrowingOOM(true);
357     ThrowException(ctx, thread, ctx.GetOutOfMemoryErrorClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
358     thread->SetThrowingOOM(false);
359     SetExceptionEvent(events::ExceptionType::NATIVE, thread);
360 }
361 
ThrowOutOfMemoryError(const PandaString &msg)362 void ThrowOutOfMemoryError(const PandaString &msg)
363 {
364     auto *thread = ManagedThread::GetCurrent();
365     ThrowOutOfMemoryError(thread, msg);
366 }
367 
ThrowUnsupportedOperationException()368 void ThrowUnsupportedOperationException()
369 {
370     auto *thread = ManagedThread::GetCurrent();
371     auto ctx = GetLanguageContext(thread);
372     ThrowException(ctx, thread, ctx.GetUnsupportedOperationExceptionClassDescriptor(), nullptr);
373 }
374 
ThrowVerificationException(const PandaString &msg)375 void ThrowVerificationException(const PandaString &msg)
376 {
377     auto *thread = ManagedThread::GetCurrent();
378     auto ctx = GetLanguageContext(thread);
379 
380     ThrowException(ctx, thread, ctx.GetVerifyErrorClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
381 }
382 
ThrowVerificationException(const LanguageContext &ctx, const PandaString &msg)383 void ThrowVerificationException(const LanguageContext &ctx, const PandaString &msg)
384 {
385     auto *thread = ManagedThread::GetCurrent();
386 
387     ThrowException(ctx, thread, ctx.GetVerifyErrorClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
388 }
389 
ThrowInstantiationError(const PandaString &msg)390 void ThrowInstantiationError(const PandaString &msg)
391 {
392     auto *thread = ManagedThread::GetCurrent();
393     auto ctx = GetLanguageContext(thread);
394 
395     ThrowException(ctx, thread, ctx.GetInstantiationErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
396     SetExceptionEvent(events::ExceptionType::INSTANTIATION_ERROR, thread);
397 }
398 
ThrowNoClassDefFoundError(const PandaString &msg)399 void ThrowNoClassDefFoundError(const PandaString &msg)
400 {
401     auto *thread = ManagedThread::GetCurrent();
402     auto ctx = GetLanguageContext(thread);
403 
404     ThrowException(ctx, thread, ctx.GetNoClassDefFoundErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
405 }
406 
ThrowTypedErrorDyn(const std::string &msg)407 void ThrowTypedErrorDyn(const std::string &msg)
408 {
409     auto *thread = ManagedThread::GetCurrent();
410     auto ctx = GetLanguageContext(thread);
411     ThrowException(ctx, thread, ctx.GetTypedErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
412 }
413 
ThrowReferenceErrorDyn(const std::string &msg)414 void ThrowReferenceErrorDyn(const std::string &msg)
415 {
416     auto *thread = ManagedThread::GetCurrent();
417     auto ctx = GetLanguageContext(thread);
418     ThrowException(ctx, thread, ctx.GetReferenceErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
419 }
420 
ThrowIllegalMonitorStateException(const PandaString &msg)421 void ThrowIllegalMonitorStateException(const PandaString &msg)
422 {
423     auto *thread = ManagedThread::GetCurrent();
424     auto ctx = GetLanguageContext(thread);
425 
426     ThrowException(ctx, thread, ctx.GetIllegalMonitorStateExceptionDescriptor(), utf::CStringAsMutf8(msg.c_str()));
427 }
428 
ThrowCloneNotSupportedException()429 void ThrowCloneNotSupportedException()
430 {
431     auto *thread = ManagedThread::GetCurrent();
432     auto ctx = GetLanguageContext(thread);
433     PandaString msg = "Class doesn't implement Cloneable";
434     ThrowException(ctx, thread, ctx.GetCloneNotSupportedExceptionDescriptor(), utf::CStringAsMutf8(msg.c_str()));
435 }
436 
HandlePendingException(UnwindPolicy policy)437 void HandlePendingException(UnwindPolicy policy)
438 {
439     auto *thread = ManagedThread::GetCurrent();
440     ASSERT(thread->HasPendingException());
441     LOG(DEBUG, INTEROP) << "HandlePendingException";
442 
443     thread->GetVM()->ClearInteropHandleScopes(thread->GetCurrentFrame());
444 
445     auto stack = StackWalker::Create(thread, policy);
446     ASSERT(stack.IsCFrame());
447 
448     FindCatchBlockInCFrames(thread, &stack, nullptr);
449 }
450 
451 }  // namespace ark
452