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
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
ThrowCoroutinesLimitExceedError(const PandaString & msg)203 void ThrowCoroutinesLimitExceedError(const PandaString &msg)
204 {
205 auto *thread = ManagedThread::GetCurrent();
206 auto ctx = GetLanguageContext(thread);
207
208 ThrowException(ctx, thread, ctx.GetCoroutinesLimitExceedErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
209 }
210
DropCFrameIfNecessary(ManagedThread * thread,StackWalker * stack,Frame * origFrame,FrameAccessor nextFrame,Method * method)211 NO_ADDRESS_SANITIZE void DropCFrameIfNecessary(ManagedThread *thread, StackWalker *stack, Frame *origFrame,
212 FrameAccessor nextFrame, Method *method)
213 {
214 if (!nextFrame.IsCFrame()) {
215 if (origFrame != nullptr) {
216 FreeFrame(origFrame);
217 }
218 thread->SetCurrentFrame(nextFrame.GetIFrame());
219 LOG(DEBUG, INTEROP) << "DropCompiledFrameAndReturn. Next frame isn't CFrame";
220 DropCompiledFrame(stack);
221 UNREACHABLE();
222 }
223
224 if (nextFrame.GetCFrame().IsNative()) {
225 if (origFrame != nullptr) {
226 FreeFrame(origFrame);
227 }
228 LOG(DEBUG, INTEROP) << "DropCompiledFrameAndReturn. Next frame is NATIVE";
229 DropCompiledFrame(stack);
230 UNREACHABLE();
231 }
232
233 if (method->IsStaticConstructor()) {
234 if (origFrame != nullptr) {
235 FreeFrame(origFrame);
236 }
237 LOG(DEBUG, INTEROP) << "DropCompiledFrameAndReturn. Next frame is StaticConstructor";
238 DropCompiledFrame(stack);
239 UNREACHABLE();
240 }
241 }
242
243 /**
244 * The function finds the corresponding catch block for the exception in the thread.
245 * The function uses thread as an exception storage because:
246 * 1. thread's exception is a GC root
247 * 2. we cannot use HandleScope here because the function is [[noreturn]]
248 */
249 // NOLINTNEXTLINE(google-runtime-references)
FindCatchBlockInCFrames(ManagedThread * thread,StackWalker * stack,Frame * origFrame)250 NO_ADDRESS_SANITIZE void FindCatchBlockInCFrames(ManagedThread *thread, StackWalker *stack, Frame *origFrame)
251 {
252 auto nextFrame = stack->GetNextFrame();
253 for (; stack->HasFrame(); stack->NextFrame(), nextFrame = stack->GetNextFrame()) {
254 LOG(DEBUG, INTEROP) << "Search for the catch block in " << stack->GetMethod()->GetFullName();
255
256 auto pc = stack->GetBytecodePc();
257 auto *method = stack->GetMethod();
258 ASSERT(method != nullptr);
259 uint32_t pcOffset = method->FindCatchBlock(thread->GetException()->ClassAddr<Class>(), pc);
260 if (pcOffset != panda_file::INVALID_OFFSET) {
261 if (origFrame != nullptr) {
262 FreeFrame(origFrame);
263 }
264
265 LOG(DEBUG, INTEROP) << "Catch block is found in " << stack->GetMethod()->GetFullName();
266 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
267 Deoptimize(stack, method->GetInstructions() + pcOffset);
268 UNREACHABLE();
269 }
270
271 thread->GetVM()->HandleReturnFrame();
272
273 DropCFrameIfNecessary(thread, stack, origFrame, nextFrame, method);
274
275 if (stack->IsInlined()) {
276 continue;
277 }
278
279 auto prev = stack->GetCFrame().GetPrevFrame();
280 if (stack->GetBoundaryFrameMethod<FrameKind::COMPILER>(prev) == FrameBridgeKind::BYPASS) {
281 /**
282 * There is bypass bridge and current frame is not inlined, hence we are going to exit compiled
283 * function. Dynamic languages may do c2c call through runtime, so it's necessary to return to exit
284 * active function properly.
285 */
286 if (origFrame != nullptr) {
287 FreeFrame(origFrame);
288 }
289 LOG(DEBUG, INTEROP) << "DropCompiledFrameAndReturn. Next frame is caller's cframe";
290 DropCompiledFrame(stack);
291 UNREACHABLE();
292 }
293 }
294
295 if (nextFrame.IsValid()) {
296 LOG(DEBUG, INTEROP) << "Exception " << thread->GetException()->ClassAddr<Class>()->GetName() << " isn't found";
297 EVENT_METHOD_EXIT(stack->GetMethod()->GetFullName(), events::MethodExitKind::COMPILED,
298 thread->RecordMethodExit());
299 thread->GetVM()->HandleReturnFrame();
300 DropCompiledFrame(stack);
301 }
302 UNREACHABLE();
303 }
304
305 /**
306 * The function finds the corresponding catch block for the exception in the thread.
307 * The function uses thread as an exception storage because:
308 * 1. thread's exception is a GC root
309 * 2. we cannot use Handl;eScope her ebecause the function is [[noreturn]]
310 */
FindCatchBlockInCallStack(ManagedThread * thread)311 NO_ADDRESS_SANITIZE void FindCatchBlockInCallStack(ManagedThread *thread)
312 {
313 auto stack = StackWalker::Create(thread);
314 auto origFrame = stack.GetIFrame();
315 ASSERT(!stack.IsCFrame());
316 LOG(DEBUG, INTEROP) << "Enter in FindCatchBlockInCallStack for " << origFrame->GetMethod()->GetFullName();
317 // Exception will be handled in the Method::Invoke's caller
318 if (origFrame->IsInvoke()) {
319 return;
320 }
321
322 stack.NextFrame();
323
324 // NATIVE frames can handle exceptions as well
325 if (!stack.HasFrame() || !stack.IsCFrame() || stack.GetCFrame().IsNative()) {
326 return;
327 }
328 thread->GetVM()->HandleReturnFrame();
329 FindCatchBlockInCFrames(thread, &stack, origFrame);
330 }
331
ThrowFileNotFoundException(const PandaString & msg)332 void ThrowFileNotFoundException(const PandaString &msg)
333 {
334 auto *thread = ManagedThread::GetCurrent();
335 auto ctx = GetLanguageContext(thread);
336
337 ThrowException(ctx, thread, ctx.GetFileNotFoundExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
338 }
339
ThrowIOException(const PandaString & msg)340 void ThrowIOException(const PandaString &msg)
341 {
342 auto *thread = ManagedThread::GetCurrent();
343 auto ctx = GetLanguageContext(thread);
344
345 ThrowException(ctx, thread, ctx.GetIOExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
346 }
347
ThrowIllegalAccessException(const PandaString & msg)348 void ThrowIllegalAccessException(const PandaString &msg)
349 {
350 auto *thread = ManagedThread::GetCurrent();
351 auto ctx = GetLanguageContext(thread);
352
353 ThrowException(ctx, thread, ctx.GetIllegalAccessExceptionClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
354 }
355
ThrowOutOfMemoryError(ManagedThread * thread,const PandaString & msg)356 void ThrowOutOfMemoryError(ManagedThread *thread, const PandaString &msg)
357 {
358 auto ctx = GetLanguageContext(thread);
359
360 if (thread->IsThrowingOOM()) {
361 // In case of OOM try to allocate exception object first,
362 // because allocator still may have some space that will be enough for allocating OOM exception.
363 // If during allocation allocator throws OOM once again, we use preallocate object without collecting stack
364 // trace.
365 thread->SetUsePreAllocObj(true);
366 }
367
368 thread->SetThrowingOOM(true);
369 ThrowException(ctx, thread, ctx.GetOutOfMemoryErrorClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
370 thread->SetThrowingOOM(false);
371 SetExceptionEvent(events::ExceptionType::NATIVE, thread);
372 }
373
ThrowOutOfMemoryError(const PandaString & msg)374 void ThrowOutOfMemoryError(const PandaString &msg)
375 {
376 auto *thread = ManagedThread::GetCurrent();
377 ThrowOutOfMemoryError(thread, msg);
378 }
379
ThrowUnsupportedOperationException()380 void ThrowUnsupportedOperationException()
381 {
382 auto *thread = ManagedThread::GetCurrent();
383 auto ctx = GetLanguageContext(thread);
384 ThrowException(ctx, thread, ctx.GetUnsupportedOperationExceptionClassDescriptor(), nullptr);
385 }
386
ThrowVerificationException(const PandaString & msg)387 void ThrowVerificationException(const PandaString &msg)
388 {
389 auto *thread = ManagedThread::GetCurrent();
390 auto ctx = GetLanguageContext(thread);
391
392 ThrowException(ctx, thread, ctx.GetVerifyErrorClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
393 }
394
ThrowVerificationException(const LanguageContext & ctx,const PandaString & msg)395 void ThrowVerificationException(const LanguageContext &ctx, const PandaString &msg)
396 {
397 auto *thread = ManagedThread::GetCurrent();
398
399 ThrowException(ctx, thread, ctx.GetVerifyErrorClassDescriptor(), utf::CStringAsMutf8(msg.c_str()));
400 }
401
ThrowInstantiationError(const PandaString & msg)402 void ThrowInstantiationError(const PandaString &msg)
403 {
404 auto *thread = ManagedThread::GetCurrent();
405 auto ctx = GetLanguageContext(thread);
406
407 ThrowException(ctx, thread, ctx.GetInstantiationErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
408 SetExceptionEvent(events::ExceptionType::INSTANTIATION_ERROR, thread);
409 }
410
ThrowNoClassDefFoundError(const PandaString & msg)411 void ThrowNoClassDefFoundError(const PandaString &msg)
412 {
413 auto *thread = ManagedThread::GetCurrent();
414 auto ctx = GetLanguageContext(thread);
415
416 ThrowException(ctx, thread, ctx.GetNoClassDefFoundErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
417 }
418
ThrowTypedErrorDyn(const std::string & msg)419 void ThrowTypedErrorDyn(const std::string &msg)
420 {
421 auto *thread = ManagedThread::GetCurrent();
422 auto ctx = GetLanguageContext(thread);
423 ThrowException(ctx, thread, ctx.GetTypedErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
424 }
425
ThrowReferenceErrorDyn(const std::string & msg)426 void ThrowReferenceErrorDyn(const std::string &msg)
427 {
428 auto *thread = ManagedThread::GetCurrent();
429 auto ctx = GetLanguageContext(thread);
430 ThrowException(ctx, thread, ctx.GetReferenceErrorDescriptor(), utf::CStringAsMutf8(msg.c_str()));
431 }
432
ThrowIllegalMonitorStateException(const PandaString & msg)433 void ThrowIllegalMonitorStateException(const PandaString &msg)
434 {
435 auto *thread = ManagedThread::GetCurrent();
436 auto ctx = GetLanguageContext(thread);
437
438 ThrowException(ctx, thread, ctx.GetIllegalMonitorStateExceptionDescriptor(), utf::CStringAsMutf8(msg.c_str()));
439 }
440
ThrowCloneNotSupportedException()441 void ThrowCloneNotSupportedException()
442 {
443 auto *thread = ManagedThread::GetCurrent();
444 auto ctx = GetLanguageContext(thread);
445 PandaString msg = "Class doesn't implement Cloneable";
446 ThrowException(ctx, thread, ctx.GetCloneNotSupportedExceptionDescriptor(), utf::CStringAsMutf8(msg.c_str()));
447 }
448
HandlePendingException(UnwindPolicy policy)449 void HandlePendingException(UnwindPolicy policy)
450 {
451 auto *thread = ManagedThread::GetCurrent();
452 ASSERT(thread != nullptr);
453 ASSERT(thread->HasPendingException());
454 LOG(DEBUG, INTEROP) << "HandlePendingException";
455
456 // NOTE(konstanting): a potential candidate for moving out of the core part
457 thread->GetVM()->CleanupCompiledFrameResources(thread->GetCurrentFrame());
458
459 auto stack = StackWalker::Create(thread, policy);
460 ASSERT(stack.IsCFrame());
461
462 FindCatchBlockInCFrames(thread, &stack, nullptr);
463 }
464
465 } // namespace ark
466