blob: f2a848161bab608bf111f48681cbd1a94e38fb0f [file] [log] [blame]
Ben Murdochc5610432016-08-08 18:44:38 +01001// 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/wasm/wasm-macro-gen.h"
6
7#include "test/cctest/cctest.h"
8#include "test/cctest/compiler/value-helper.h"
9#include "test/cctest/wasm/test-signatures.h"
10#include "test/cctest/wasm/wasm-run-utils.h"
11
12using namespace v8::base;
13using namespace v8::internal;
14using namespace v8::internal::compiler;
15using namespace v8::internal::wasm;
16
17using v8::Local;
18using v8::Utils;
19
20namespace {
21
22#define CHECK_CSTREQ(exp, found) \
23 do { \
24 const char* exp_ = (exp); \
25 const char* found_ = (found); \
26 DCHECK_NOT_NULL(exp); \
27 if (V8_UNLIKELY(found_ == nullptr || strcmp(exp_, found_) != 0)) { \
28 V8_Fatal(__FILE__, __LINE__, \
29 "Check failed: (%s) != (%s) ('%s' vs '%s').", #exp, #found, \
30 exp_, found_ ? found_ : "<null>"); \
31 } \
32 } while (0)
33
34void PrintStackTrace(v8::Local<v8::StackTrace> stack) {
35 printf("Stack Trace (length %d):\n", stack->GetFrameCount());
36 for (int i = 0, e = stack->GetFrameCount(); i != e; ++i) {
37 v8::Local<v8::StackFrame> frame = stack->GetFrame(i);
38 v8::Local<v8::String> script = frame->GetScriptName();
39 v8::Local<v8::String> func = frame->GetFunctionName();
40 printf("[%d] (%s) %s:%d:%d\n", i,
41 script.IsEmpty() ? "<null>" : *v8::String::Utf8Value(script),
42 func.IsEmpty() ? "<null>" : *v8::String::Utf8Value(func),
43 frame->GetLineNumber(), frame->GetColumn());
44 }
45}
46
47struct ExceptionInfo {
48 const char* func_name;
49 int line_nr;
50 int column;
51};
52
53template <int N>
Ben Murdoch61f157c2016-09-16 13:49:30 +010054void CheckExceptionInfos(Handle<Object> exc,
Ben Murdochc5610432016-08-08 18:44:38 +010055 const ExceptionInfo (&excInfos)[N]) {
56 // Check that it's indeed an Error object.
Ben Murdoch61f157c2016-09-16 13:49:30 +010057 CHECK(exc->IsJSError());
Ben Murdochc5610432016-08-08 18:44:38 +010058
59 // Extract stack frame from the exception.
60 Local<v8::Value> localExc = Utils::ToLocal(exc);
61 v8::Local<v8::StackTrace> stack = v8::Exception::GetStackTrace(localExc);
62 PrintStackTrace(stack);
63 CHECK(!stack.IsEmpty());
64 CHECK_EQ(N, stack->GetFrameCount());
65
66 for (int frameNr = 0; frameNr < N; ++frameNr) {
67 v8::Local<v8::StackFrame> frame = stack->GetFrame(frameNr);
68 v8::String::Utf8Value funName(frame->GetFunctionName());
69 CHECK_CSTREQ(excInfos[frameNr].func_name, *funName);
70 CHECK_EQ(excInfos[frameNr].line_nr, frame->GetLineNumber());
71 CHECK_EQ(excInfos[frameNr].column, frame->GetColumn());
72 }
73}
74
75} // namespace
76
77// Call from JS to WASM to JS and throw an Error from JS.
78TEST(CollectDetailedWasmStack_ExplicitThrowFromJs) {
79 TestSignatures sigs;
80 TestingModule module;
81
82 // Initialize WasmFunctionCompiler first, since it sets up the HandleScope.
83 WasmFunctionCompiler comp1(sigs.v_v(), &module);
84
85 uint32_t js_throwing_index = module.AddJsFunction(
86 sigs.v_v(),
87 "(function js() {\n function a() {\n throw new Error(); };\n a(); })");
88
89 // Add a nop such that we don't always get position 1.
90 BUILD(comp1, WASM_NOP, WASM_CALL_FUNCTION0(js_throwing_index));
91 uint32_t wasm_index = comp1.CompileAndAdd();
92
93 WasmFunctionCompiler comp2(sigs.v_v(), &module);
94 BUILD(comp2, WASM_CALL_FUNCTION0(wasm_index));
95 uint32_t wasm_index_2 = comp2.CompileAndAdd();
96
97 Handle<JSFunction> js_wasm_wrapper = module.WrapCode(wasm_index_2);
98
99 Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast(
100 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
101 CompileRun("(function callFn(fn) { fn(); })"))));
102
103 Isolate* isolate = js_wasm_wrapper->GetIsolate();
104 isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10,
105 v8::StackTrace::kOverview);
106 Handle<Object> global(isolate->context()->global_object(), isolate);
107 MaybeHandle<Object> maybe_exc;
108 Handle<Object> args[] = {js_wasm_wrapper};
109 MaybeHandle<Object> returnObjMaybe =
110 Execution::TryCall(isolate, js_trampoline, global, 1, args, &maybe_exc);
111 CHECK(returnObjMaybe.is_null());
112
Ben Murdoch61f157c2016-09-16 13:49:30 +0100113 // The column is 1-based, so add 1 to the actual byte offset.
Ben Murdochc5610432016-08-08 18:44:38 +0100114 ExceptionInfo expected_exceptions[] = {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100115 {"a", 3, 8}, // -
116 {"js", 4, 2}, // -
117 {"<WASM UNNAMED>", static_cast<int>(wasm_index), 3}, // -
118 {"<WASM UNNAMED>", static_cast<int>(wasm_index_2), 2}, // -
119 {"callFn", 1, 24} // -
Ben Murdochc5610432016-08-08 18:44:38 +0100120 };
Ben Murdoch61f157c2016-09-16 13:49:30 +0100121 CheckExceptionInfos(maybe_exc.ToHandleChecked(), expected_exceptions);
Ben Murdochc5610432016-08-08 18:44:38 +0100122}
123
124// Trigger a trap in WASM, stack should be JS -> WASM -> WASM.
125TEST(CollectDetailedWasmStack_WasmError) {
126 TestSignatures sigs;
127 TestingModule module;
128
129 WasmFunctionCompiler comp1(sigs.i_v(), &module,
130 ArrayVector("exec_unreachable"));
131 // Set the execution context, such that a runtime error can be thrown.
132 comp1.SetModuleContext();
133 BUILD(comp1, WASM_UNREACHABLE);
134 uint32_t wasm_index = comp1.CompileAndAdd();
135
136 WasmFunctionCompiler comp2(sigs.i_v(), &module,
137 ArrayVector("call_exec_unreachable"));
138 BUILD(comp2, WASM_CALL_FUNCTION0(wasm_index));
139 uint32_t wasm_index_2 = comp2.CompileAndAdd();
140
141 Handle<JSFunction> js_wasm_wrapper = module.WrapCode(wasm_index_2);
142
143 Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast(
144 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
145 CompileRun("(function callFn(fn) { fn(); })"))));
146
147 Isolate* isolate = js_wasm_wrapper->GetIsolate();
148 isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10,
149 v8::StackTrace::kOverview);
150 Handle<Object> global(isolate->context()->global_object(), isolate);
151 MaybeHandle<Object> maybe_exc;
152 Handle<Object> args[] = {js_wasm_wrapper};
153 MaybeHandle<Object> maybe_return_obj =
154 Execution::TryCall(isolate, js_trampoline, global, 1, args, &maybe_exc);
155 CHECK(maybe_return_obj.is_null());
156
Ben Murdoch61f157c2016-09-16 13:49:30 +0100157 // The column is 1-based, so add 1 to the actual byte offset.
Ben Murdochc5610432016-08-08 18:44:38 +0100158 ExceptionInfo expected_exceptions[] = {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100159 {"<WASM UNNAMED>", static_cast<int>(wasm_index), 2}, // -
160 {"<WASM UNNAMED>", static_cast<int>(wasm_index_2), 2}, // -
161 {"callFn", 1, 24} //-
Ben Murdochc5610432016-08-08 18:44:38 +0100162 };
Ben Murdoch61f157c2016-09-16 13:49:30 +0100163 CheckExceptionInfos(maybe_exc.ToHandleChecked(), expected_exceptions);
Ben Murdochc5610432016-08-08 18:44:38 +0100164}