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/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 34struct ExceptionInfo { 35 const char* func_name; 36 int line_nr; 37 int column; 38}; 39 40template <int N> 41void CheckExceptionInfos(Handle<Object> exc, 42 const ExceptionInfo (&excInfos)[N]) { 43 // Check that it's indeed an Error object. 44 CHECK(exc->IsJSError()); 45 46 // Extract stack frame from the exception. 47 Local<v8::Value> localExc = Utils::ToLocal(exc); 48 v8::Local<v8::StackTrace> stack = v8::Exception::GetStackTrace(localExc); 49 CHECK(!stack.IsEmpty()); 50 CHECK_EQ(N, stack->GetFrameCount()); 51 52 for (int frameNr = 0; frameNr < N; ++frameNr) { 53 v8::Local<v8::StackFrame> frame = stack->GetFrame(frameNr); 54 v8::String::Utf8Value funName(frame->GetFunctionName()); 55 CHECK_CSTREQ(excInfos[frameNr].func_name, *funName); 56 CHECK_EQ(excInfos[frameNr].line_nr, frame->GetLineNumber()); 57 CHECK_EQ(excInfos[frameNr].column, frame->GetColumn()); 58 } 59} 60 61} // namespace 62 63// Trigger a trap for executing unreachable. 64TEST(Unreachable) { 65 TestSignatures sigs; 66 TestingModule module; 67 68 WasmFunctionCompiler comp1(sigs.v_v(), &module, 69 ArrayVector("exec_unreachable")); 70 // Set the execution context, such that a runtime error can be thrown. 71 comp1.SetModuleContext(); 72 BUILD(comp1, WASM_UNREACHABLE); 73 uint32_t wasm_index = comp1.CompileAndAdd(); 74 75 Handle<JSFunction> js_wasm_wrapper = module.WrapCode(wasm_index); 76 77 Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast( 78 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( 79 CompileRun("(function callFn(fn) { fn(); })")))); 80 81 Isolate* isolate = js_wasm_wrapper->GetIsolate(); 82 isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10, 83 v8::StackTrace::kOverview); 84 Handle<Object> global(isolate->context()->global_object(), isolate); 85 MaybeHandle<Object> maybe_exc; 86 Handle<Object> args[] = {js_wasm_wrapper}; 87 MaybeHandle<Object> returnObjMaybe = 88 Execution::TryCall(isolate, js_trampoline, global, 1, args, &maybe_exc); 89 CHECK(returnObjMaybe.is_null()); 90 91 // The column is 1-based, so add 1 to the actual byte offset. 92 ExceptionInfo expected_exceptions[] = { 93 {"<WASM UNNAMED>", static_cast<int>(wasm_index), 2}, // -- 94 {"callFn", 1, 24} // -- 95 }; 96 CheckExceptionInfos(maybe_exc.ToHandleChecked(), expected_exceptions); 97} 98 99// Trigger a trap for loading from out-of-bounds. 100TEST(IllegalLoad) { 101 TestSignatures sigs; 102 TestingModule module; 103 104 WasmFunctionCompiler comp1(sigs.v_v(), &module, ArrayVector("mem_oob")); 105 // Set the execution context, such that a runtime error can be thrown. 106 comp1.SetModuleContext(); 107 BUILD(comp1, WASM_IF(WASM_ONE, 108 WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(-3)))); 109 uint32_t wasm_index = comp1.CompileAndAdd(); 110 111 WasmFunctionCompiler comp2(sigs.v_v(), &module, ArrayVector("call_mem_oob")); 112 // Insert a NOP such that the position of the call is not one. 113 BUILD(comp2, WASM_NOP, WASM_CALL_FUNCTION0(wasm_index)); 114 uint32_t wasm_index_2 = comp2.CompileAndAdd(); 115 116 Handle<JSFunction> js_wasm_wrapper = module.WrapCode(wasm_index_2); 117 118 Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast( 119 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( 120 CompileRun("(function callFn(fn) { fn(); })")))); 121 122 Isolate* isolate = js_wasm_wrapper->GetIsolate(); 123 isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10, 124 v8::StackTrace::kOverview); 125 Handle<Object> global(isolate->context()->global_object(), isolate); 126 MaybeHandle<Object> maybe_exc; 127 Handle<Object> args[] = {js_wasm_wrapper}; 128 MaybeHandle<Object> returnObjMaybe = 129 Execution::TryCall(isolate, js_trampoline, global, 1, args, &maybe_exc); 130 CHECK(returnObjMaybe.is_null()); 131 132 // The column is 1-based, so add 1 to the actual byte offset. 133 ExceptionInfo expected_exceptions[] = { 134 {"<WASM UNNAMED>", static_cast<int>(wasm_index), 7}, // -- 135 {"<WASM UNNAMED>", static_cast<int>(wasm_index_2), 3}, // -- 136 {"callFn", 1, 24} // -- 137 }; 138 CheckExceptionInfos(maybe_exc.ToHandleChecked(), expected_exceptions); 139} 140