v8/test/cctest/wasm/test-run-wasm-exceptions.cc
Ben Smith 1a88414c41 [wasm] Call through dispatch table in interpreter
When running wasm tests, the interpreter previously used a static
collection of function indexes stored in WasmTable to perform
call_indirect calls internal to that module. This has the wrong behavior
if the table is changed (via WasmTableObject::Set, `table.copy`, or
`table.init`).

This CL changes the cctests to always generate an intepreter entry for
all functions, and stores those entries in the dispatch table. This
allows us to use the same execution path as for non-testing code.

The interpreter entry compiler needed to be changed to support
multi-value returns too, since a 64-bit integer return value may be
lowered to two 32-bit integer returns.

Bug: v8:9016
Change-Id: I277df21ffde5c2eee0b691fcc9bab2b1a43eeffc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1531137
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60380}
2019-03-21 07:35:27 +00:00

220 lines
8.2 KiB
C++

// 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 "src/api-inl.h"
#include "test/cctest/wasm/wasm-atomics-utils.h"
#include "test/common/wasm/test-signatures.h"
#include "test/common/wasm/wasm-macro-gen.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace test_run_wasm_exceptions {
WASM_EXEC_TEST(TryCatchThrow) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmRunner<uint32_t, uint32_t> r(execution_tier);
uint32_t except = r.builder().AddException(sigs.v_v());
constexpr uint32_t kResult0 = 23;
constexpr uint32_t kResult1 = 42;
// Build the main test function.
BUILD(r, WASM_TRY_CATCH_T(kWasmI32,
WASM_STMTS(WASM_I32V(kResult1),
WASM_IF(WASM_I32_EQZ(WASM_GET_LOCAL(0)),
WASM_THROW(except))),
WASM_STMTS(WASM_DROP, WASM_I32V(kResult0))));
// Need to call through JS to allow for creation of stack traces.
r.CheckCallViaJS(kResult0, 0);
r.CheckCallViaJS(kResult1, 1);
}
WASM_EXEC_TEST(TryCatchCallDirect) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmRunner<uint32_t, uint32_t> r(execution_tier);
uint32_t except = r.builder().AddException(sigs.v_v());
constexpr uint32_t kResult0 = 23;
constexpr uint32_t kResult1 = 42;
// Build a throwing helper function.
WasmFunctionCompiler& throw_func = r.NewFunction(sigs.i_ii());
BUILD(throw_func, WASM_THROW(except));
// Build the main test function.
BUILD(r, WASM_TRY_CATCH_T(
kWasmI32,
WASM_STMTS(WASM_I32V(kResult1),
WASM_IF(WASM_I32_EQZ(WASM_GET_LOCAL(0)),
WASM_STMTS(WASM_CALL_FUNCTION(
throw_func.function_index(),
WASM_I32V(7), WASM_I32V(9)),
WASM_DROP))),
WASM_STMTS(WASM_DROP, WASM_I32V(kResult0))));
// Need to call through JS to allow for creation of stack traces.
r.CheckCallViaJS(kResult0, 0);
r.CheckCallViaJS(kResult1, 1);
}
WASM_EXEC_TEST(TryCatchCallIndirect) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmRunner<uint32_t, uint32_t> r(execution_tier);
uint32_t except = r.builder().AddException(sigs.v_v());
constexpr uint32_t kResult0 = 23;
constexpr uint32_t kResult1 = 42;
// Build a throwing helper function.
WasmFunctionCompiler& throw_func = r.NewFunction(sigs.i_ii());
BUILD(throw_func, WASM_THROW(except));
r.builder().AddSignature(sigs.i_ii());
throw_func.SetSigIndex(0);
// Add an indirect function table.
uint16_t indirect_function_table[] = {
static_cast<uint16_t>(throw_func.function_index())};
r.builder().AddIndirectFunctionTable(indirect_function_table,
arraysize(indirect_function_table));
// Build the main test function.
BUILD(r, WASM_TRY_CATCH_T(
kWasmI32,
WASM_STMTS(WASM_I32V(kResult1),
WASM_IF(WASM_I32_EQZ(WASM_GET_LOCAL(0)),
WASM_STMTS(WASM_CALL_INDIRECT2(
0, WASM_GET_LOCAL(0),
WASM_I32V(7), WASM_I32V(9)),
WASM_DROP))),
WASM_STMTS(WASM_DROP, WASM_I32V(kResult0))));
// Need to call through JS to allow for creation of stack traces.
r.CheckCallViaJS(kResult0, 0);
r.CheckCallViaJS(kResult1, 1);
}
WASM_EXEC_TEST(TryCatchCallExternal) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
HandleScope scope(CcTest::InitIsolateOnce());
const char* source = "(function() { throw 'ball'; })";
Handle<JSFunction> js_function =
Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CompileRun(source))));
ManuallyImportedJSFunction import = {sigs.i_ii(), js_function};
WasmRunner<uint32_t, uint32_t> r(execution_tier, &import);
constexpr uint32_t kResult0 = 23;
constexpr uint32_t kResult1 = 42;
constexpr uint32_t kJSFunc = 0;
// Build the main test function.
BUILD(r, WASM_TRY_CATCH_T(
kWasmI32,
WASM_STMTS(
WASM_I32V(kResult1),
WASM_IF(WASM_I32_EQZ(WASM_GET_LOCAL(0)),
WASM_STMTS(WASM_CALL_FUNCTION(kJSFunc, WASM_I32V(7),
WASM_I32V(9)),
WASM_DROP))),
WASM_STMTS(WASM_DROP, WASM_I32V(kResult0))));
// Need to call through JS to allow for creation of stack traces.
r.CheckCallViaJS(kResult0, 0);
r.CheckCallViaJS(kResult1, 1);
}
WASM_EXEC_TEST(TryCatchTrapTypeError) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
HandleScope scope(CcTest::InitIsolateOnce());
const char* source = "(function() { return 0; })";
Handle<JSFunction> js_function =
Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CompileRun(source))));
// Make sure to use a signature incompatible with JS below.
ManuallyImportedJSFunction import = {sigs.i_ll(), js_function};
WasmRunner<uint32_t, uint32_t> r(execution_tier, &import);
constexpr uint32_t kResult0 = 23;
constexpr uint32_t kResult1 = 42;
constexpr uint32_t kJSFunc = 0;
// Build the main test function.
BUILD(r, WASM_TRY_CATCH_T(
kWasmI32,
WASM_STMTS(
WASM_I32V(kResult1),
WASM_IF(WASM_I32_EQZ(WASM_GET_LOCAL(0)),
WASM_STMTS(WASM_CALL_FUNCTION(kJSFunc, WASM_I64V(7),
WASM_I64V(9)),
WASM_DROP))),
WASM_STMTS(WASM_DROP, WASM_I32V(kResult0))));
// Need to call through JS to allow for creation of stack traces.
r.CheckCallViaJS(kResult0, 0);
r.CheckCallViaJS(kResult1, 1);
}
namespace {
// TODO(8729): The semantics of this are not yet specified and might change,
// this test aims at keeping semantics of various execution tiers consistent.
void TestTryCatchTrap(byte* code, size_t code_size,
ExecutionTier execution_tier) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmRunner<uint32_t, uint32_t> r(execution_tier, nullptr, "main",
kRuntimeExceptionSupport);
r.builder().AddMemory(kWasmPageSize);
constexpr uint32_t kResult0 = 23;
constexpr uint32_t kResult1 = 42;
// Build a trapping helper function.
WasmFunctionCompiler& trap_func = r.NewFunction(sigs.i_ii());
trap_func.Build(code, code + code_size);
// Build the main test function.
BUILD(r, WASM_TRY_CATCH_T(
kWasmI32,
WASM_STMTS(WASM_I32V(kResult1),
WASM_IF(WASM_I32_EQZ(WASM_GET_LOCAL(0)),
WASM_STMTS(WASM_CALL_FUNCTION(
trap_func.function_index(),
WASM_I32V(7), WASM_I32V(9)),
WASM_DROP))),
WASM_STMTS(WASM_DROP, WASM_I32V(kResult0))));
// Need to call through JS to allow for creation of stack traces.
r.CheckCallViaJS(kResult0, 0);
r.CheckCallViaJS(kResult1, 1);
}
} // namespace
WASM_EXEC_TEST(TryCatchTrapUnreachable) {
byte code[] = {WASM_UNREACHABLE};
TestTryCatchTrap(code, arraysize(code), execution_tier);
}
WASM_EXEC_TEST(TryCatchTrapMemOutOfBounds) {
byte code[] = {WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(-1))};
TestTryCatchTrap(code, arraysize(code), execution_tier);
}
WASM_EXEC_TEST(TryCatchTrapDivByZero) {
byte code[] = {WASM_I32_DIVS(WASM_GET_LOCAL(0), WASM_I32V_1(0))};
TestTryCatchTrap(code, arraysize(code), execution_tier);
}
WASM_EXEC_TEST(TryCatchTrapRemByZero) {
byte code[] = {WASM_I32_REMS(WASM_GET_LOCAL(0), WASM_I32V_1(0))};
TestTryCatchTrap(code, arraysize(code), execution_tier);
}
} // namespace test_run_wasm_exceptions
} // namespace wasm
} // namespace internal
} // namespace v8