Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/api-inl.h"
6 : #include "src/assembler-inl.h"
7 : #include "src/trap-handler/trap-handler.h"
8 : #include "test/cctest/cctest.h"
9 : #include "test/cctest/compiler/value-helper.h"
10 : #include "test/cctest/wasm/wasm-run-utils.h"
11 : #include "test/common/wasm/test-signatures.h"
12 : #include "test/common/wasm/wasm-macro-gen.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 : namespace wasm {
17 : namespace test_wasm_trap_position {
18 :
19 : using v8::Local;
20 : using v8::Utils;
21 :
22 : namespace {
23 :
24 : #define CHECK_CSTREQ(exp, found) \
25 : do { \
26 : const char* exp_ = (exp); \
27 : const char* found_ = (found); \
28 : DCHECK_NOT_NULL(exp); \
29 : if (V8_UNLIKELY(found_ == nullptr || strcmp(exp_, found_) != 0)) { \
30 : V8_Fatal(__FILE__, __LINE__, \
31 : "Check failed: (%s) != (%s) ('%s' vs '%s').", #exp, #found, \
32 : exp_, found_ ? found_ : "<null>"); \
33 : } \
34 : } while (false)
35 :
36 : struct ExceptionInfo {
37 : const char* func_name;
38 : int line_nr;
39 : int column;
40 : };
41 :
42 : template <int N>
43 30 : void CheckExceptionInfos(v8::internal::Isolate* i_isolate, Handle<Object> exc,
44 : const ExceptionInfo (&excInfos)[N]) {
45 : // Check that it's indeed an Error object.
46 60 : CHECK(exc->IsJSError());
47 : v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(i_isolate);
48 :
49 60 : exc->Print();
50 : // Extract stack frame from the exception.
51 30 : Local<v8::Value> localExc = Utils::ToLocal(exc);
52 30 : v8::Local<v8::StackTrace> stack = v8::Exception::GetStackTrace(localExc);
53 30 : CHECK(!stack.IsEmpty());
54 30 : CHECK_EQ(N, stack->GetFrameCount());
55 :
56 75 : for (int frameNr = 0; frameNr < N; ++frameNr) {
57 75 : v8::Local<v8::StackFrame> frame = stack->GetFrame(v8_isolate, frameNr);
58 150 : v8::String::Utf8Value funName(v8_isolate, frame->GetFunctionName());
59 75 : CHECK_CSTREQ(excInfos[frameNr].func_name, *funName);
60 75 : CHECK_EQ(excInfos[frameNr].line_nr, frame->GetLineNumber());
61 75 : CHECK_EQ(excInfos[frameNr].column, frame->GetColumn());
62 : }
63 30 : }
64 :
65 : #undef CHECK_CSTREQ
66 :
67 : } // namespace
68 :
69 : // Trigger a trap for executing unreachable.
70 28367 : WASM_EXEC_TEST(Unreachable) {
71 : // Create a WasmRunner with stack checks and traps enabled.
72 15 : WasmRunner<void> r(execution_tier, nullptr, "main", kRuntimeExceptionSupport);
73 15 : TestSignatures sigs;
74 :
75 15 : BUILD(r, WASM_UNREACHABLE);
76 15 : uint32_t wasm_index = r.function()->func_index;
77 :
78 15 : Handle<JSFunction> js_wasm_wrapper = r.builder().WrapCode(wasm_index);
79 :
80 : Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast(
81 : v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
82 15 : CompileRun("(function callFn(fn) { fn(); })"))));
83 :
84 : Isolate* isolate = js_wasm_wrapper->GetIsolate();
85 : isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10,
86 15 : v8::StackTrace::kOverview);
87 30 : Handle<Object> global(isolate->context()->global_object(), isolate);
88 15 : MaybeHandle<Object> maybe_exc;
89 : Handle<Object> args[] = {js_wasm_wrapper};
90 : MaybeHandle<Object> returnObjMaybe =
91 : Execution::TryCall(isolate, js_trampoline, global, 1, args,
92 15 : Execution::MessageHandling::kReport, &maybe_exc);
93 15 : CHECK(returnObjMaybe.is_null());
94 :
95 : // Line and column are 1-based, so add 1 for the expected wasm output.
96 : ExceptionInfo expected_exceptions[] = {
97 15 : {"main", static_cast<int>(wasm_index) + 1, 2}, // --
98 : {"callFn", 1, 24} // --
99 30 : };
100 : CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
101 15 : expected_exceptions);
102 15 : }
103 :
104 : // Trigger a trap for loading from out-of-bounds.
105 28367 : WASM_EXEC_TEST(IllegalLoad) {
106 15 : WasmRunner<void> r(execution_tier, nullptr, "main", kRuntimeExceptionSupport);
107 15 : TestSignatures sigs;
108 :
109 15 : r.builder().AddMemory(0L);
110 :
111 15 : BUILD(r, WASM_IF(WASM_ONE, WASM_SEQ(WASM_LOAD_MEM(MachineType::Int32(),
112 : WASM_I32V_1(-3)),
113 : WASM_DROP)));
114 15 : uint32_t wasm_index_1 = r.function()->func_index;
115 :
116 30 : WasmFunctionCompiler& f2 = r.NewFunction<void>("call_main");
117 : // Insert a NOP such that the position of the call is not one.
118 15 : BUILD(f2, WASM_NOP, WASM_CALL_FUNCTION0(wasm_index_1));
119 : uint32_t wasm_index_2 = f2.function_index();
120 :
121 15 : Handle<JSFunction> js_wasm_wrapper = r.builder().WrapCode(wasm_index_2);
122 :
123 : Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast(
124 : v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
125 15 : CompileRun("(function callFn(fn) { fn(); })"))));
126 :
127 : Isolate* isolate = js_wasm_wrapper->GetIsolate();
128 : isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10,
129 15 : v8::StackTrace::kOverview);
130 30 : Handle<Object> global(isolate->context()->global_object(), isolate);
131 15 : MaybeHandle<Object> maybe_exc;
132 : Handle<Object> args[] = {js_wasm_wrapper};
133 : MaybeHandle<Object> returnObjMaybe =
134 : Execution::TryCall(isolate, js_trampoline, global, 1, args,
135 15 : Execution::MessageHandling::kReport, &maybe_exc);
136 15 : CHECK(returnObjMaybe.is_null());
137 :
138 : // Line and column are 1-based, so add 1 for the expected wasm output.
139 : ExceptionInfo expected_exceptions[] = {
140 15 : {"main", static_cast<int>(wasm_index_1) + 1, 8}, // --
141 15 : {"call_main", static_cast<int>(wasm_index_2) + 1, 3}, // --
142 : {"callFn", 1, 24} // --
143 45 : };
144 : CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
145 15 : expected_exceptions);
146 15 : }
147 :
148 : } // namespace test_wasm_trap_position
149 : } // namespace wasm
150 : } // namespace internal
151 85011 : } // namespace v8
|