diff --git a/src/utils/vector.h b/src/utils/vector.h index 5b6c878e34..dd5c59e553 100644 --- a/src/utils/vector.h +++ b/src/utils/vector.h @@ -230,6 +230,8 @@ constexpr Vector StaticCharVector(const char (&array)[N]) { return Vector::cast(Vector(array, N - 1)); } +// The resulting vector does not contain a null-termination byte. If you want +// the null byte, use ArrayVector("foo"). inline Vector CStrVector(const char* data) { return Vector(data, strlen(data)); } @@ -250,6 +252,9 @@ inline Vector MutableCStrVector(char* data, size_t max) { return Vector(data, strnlen(data, max)); } +// For string literals, ArrayVector("foo") returns a vector ['f', 'o', 'o', \0] +// with length 4 and null-termination. +// If you want ['f', 'o', 'o'], use CStrVector("foo"). template inline constexpr Vector ArrayVector(T (&arr)[N]) { return Vector{arr, N}; diff --git a/src/wasm/c-api.cc b/src/wasm/c-api.cc index a227bfa03e..a467542f95 100644 --- a/src/wasm/c-api.cc +++ b/src/wasm/c-api.cc @@ -1134,8 +1134,12 @@ auto ExportType::type() const -> const ExternType* { i::Handle VecToString(i::Isolate* isolate, const vec& chars) { + size_t length = chars.size(); + // Some, but not all, {chars} vectors we get here are null-terminated, + // so let's be robust to that. + if (length > 0 && chars[length - 1] == 0) length--; return isolate->factory() - ->NewStringFromUtf8({chars.get(), chars.size()}) + ->NewStringFromUtf8({chars.get(), length}) .ToHandleChecked(); } diff --git a/test/wasm-api-tests/BUILD.gn b/test/wasm-api-tests/BUILD.gn index 1daad1ed78..d0e2c01ac9 100644 --- a/test/wasm-api-tests/BUILD.gn +++ b/test/wasm-api-tests/BUILD.gn @@ -38,6 +38,7 @@ v8_executable("wasm_api_tests") { "serialize.cc", "table.cc", "threads.cc", + "traps.cc", "wasm-api-test.h", ] } diff --git a/test/wasm-api-tests/callbacks.cc b/test/wasm-api-tests/callbacks.cc index f6c0e8baa0..960fa726dd 100644 --- a/test/wasm-api-tests/callbacks.cc +++ b/test/wasm-api-tests/callbacks.cc @@ -43,7 +43,7 @@ class WasmCapiCallbacksTest : public WasmCapiTest { // Build the following function: // int32 stage1(int32 arg0) { return stage2(arg0); } uint32_t stage2_index = - builder()->AddImport(ArrayVector("stage2"), wasm_i_i_sig()); + builder()->AddImport(CStrVector("stage2"), wasm_i_i_sig()); byte code[] = {WASM_CALL_FUNCTION(stage2_index, WASM_GET_LOCAL(0))}; AddExportedFunction(CStrVector("stage1"), code, sizeof(code)); @@ -81,7 +81,7 @@ TEST_F(WasmCapiCallbacksTest, GC) { // Build the following function: // int32 stage3_to4(int32 arg0) { return stage4(arg0); } uint32_t stage4_index = - builder()->AddImport(ArrayVector("stage4"), wasm_i_i_sig()); + builder()->AddImport(CStrVector("stage4"), wasm_i_i_sig()); byte code[] = {WASM_CALL_FUNCTION(stage4_index, WASM_GET_LOCAL(0))}; AddExportedFunction(CStrVector("stage3_to4"), code, sizeof(code)); @@ -134,7 +134,7 @@ TEST_F(WasmCapiTest, Recursion) { // return fibonacci_c(arg0 - 1) + fibonacci_c(arg0 - 2); // } uint32_t fibo_c_index = - builder()->AddImport(ArrayVector("fibonacci_c"), wasm_i_i_sig()); + builder()->AddImport(CStrVector("fibonacci_c"), wasm_i_i_sig()); byte code_fibo[] = { WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_ZERO), WASM_RETURN1(WASM_ZERO)), diff --git a/test/wasm-api-tests/traps.cc b/test/wasm-api-tests/traps.cc new file mode 100644 index 0000000000..b049d09330 --- /dev/null +++ b/test/wasm-api-tests/traps.cc @@ -0,0 +1,60 @@ +// Copyright 2019 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "test/wasm-api-tests/wasm-api-test.h" + +#include + +namespace v8 { +namespace internal { +namespace wasm { + +using ::wasm::Message; + +namespace { + +own FailCallback(void* env, const Val args[], Val results[]) { + Store* store = reinterpret_cast(env); + Message message = Message::make(std::string("callback abort")); + return Trap::make(store, message); +} + +void ExpectMessage(const char* expected, const Message& message) { + size_t len = strlen(expected); + EXPECT_EQ(len, message.size()); + EXPECT_EQ(0, strncmp(expected, message.get(), len)); +} + +} // namespace + +TEST_F(WasmCapiTest, Traps) { + ValueType i32_type[] = {kWasmI32}; + FunctionSig sig(1, 0, i32_type); + uint32_t callback_index = builder()->AddImport(CStrVector("callback"), &sig); + byte code[] = {WASM_CALL_FUNCTION0(callback_index)}; + AddExportedFunction(CStrVector("callback"), code, sizeof(code), &sig); + byte code2[] = {WASM_UNREACHABLE, WASM_I32V_1(1)}; + AddExportedFunction(CStrVector("unreachable"), code2, sizeof(code2), &sig); + + own func_type = FuncType::make( + vec::make(), vec::make(ValType::make(::wasm::I32))); + own cpp_callback = Func::make(store(), func_type.get(), FailCallback, + reinterpret_cast(store())); + Extern* imports[] = {cpp_callback.get()}; + Instantiate(imports); + + Func* cpp_trapping_func = GetExportedFunction(0); + own cpp_trap = cpp_trapping_func->call(); + EXPECT_NE(nullptr, cpp_trap.get()); + ExpectMessage("Uncaught Error: callback abort", cpp_trap->message()); + + Func* wasm_trapping_func = GetExportedFunction(1); + own wasm_trap = wasm_trapping_func->call(); + EXPECT_NE(nullptr, wasm_trap.get()); + ExpectMessage("Uncaught RuntimeError: unreachable", wasm_trap->message()); +} + +} // namespace wasm +} // namespace internal +} // namespace v8