v8/test/common/wasm/wasm-module-runner.cc
Michael Starzinger 224ca74ae4 [asm.js] Propagate language mode to exported functions.
This makes sure the language mode of the module is correctly propagated
through the WebAssembly module, so that exported functions are allocated
with the correct language mode. It extends the existing {ModuleOrigin}
enum to consist of three values now.

R=clemensh@chromium.org
TEST=mjsunit/regress/wasm/regress-985154
BUG=chromium:985154

Change-Id: Id7b566738b1e710cc5001b894022bcd0f2c01bc3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1708484
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62826}
2019-07-19 11:47:48 +00:00

283 lines
11 KiB
C++

// Copyright 2016 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/common/wasm/wasm-module-runner.h"
#include "src/execution/isolate.h"
#include "src/handles/handles.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/property-descriptor.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-interpreter.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace testing {
uint32_t GetInitialMemSize(const WasmModule* module) {
return kWasmPageSize * module->initial_pages;
}
MaybeHandle<WasmModuleObject> CompileForTesting(Isolate* isolate,
ErrorThrower* thrower,
const ModuleWireBytes& bytes) {
auto enabled_features = WasmFeaturesFromIsolate(isolate);
MaybeHandle<WasmModuleObject> module = isolate->wasm_engine()->SyncCompile(
isolate, enabled_features, thrower, bytes);
DCHECK_EQ(thrower->error(), module.is_null());
return module;
}
MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting(
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes) {
MaybeHandle<WasmModuleObject> module =
CompileForTesting(isolate, thrower, bytes);
if (module.is_null()) return {};
return isolate->wasm_engine()->SyncInstantiate(
isolate, thrower, module.ToHandleChecked(), {}, {});
}
std::shared_ptr<WasmModule> DecodeWasmModuleForTesting(
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
const byte* module_end, ModuleOrigin origin, bool verify_functions) {
// Decode the module, but don't verify function bodies, since we'll
// be compiling them anyway.
auto enabled_features = WasmFeaturesFromIsolate(isolate);
ModuleResult decoding_result = DecodeWasmModule(
enabled_features, module_start, module_end, verify_functions, origin,
isolate->counters(), isolate->wasm_engine()->allocator());
if (decoding_result.failed()) {
// Module verification failed. throw.
thrower->CompileError("DecodeWasmModule failed: %s",
decoding_result.error().message().c_str());
}
return std::move(decoding_result).value();
}
bool InterpretWasmModuleForTesting(Isolate* isolate,
Handle<WasmInstanceObject> instance,
const char* name, size_t argc,
WasmValue* args) {
HandleScope handle_scope(isolate); // Avoid leaking handles.
WasmCodeRefScope code_ref_scope;
MaybeHandle<WasmExportedFunction> maybe_function =
GetExportedFunction(isolate, instance, "main");
Handle<WasmExportedFunction> function;
if (!maybe_function.ToHandle(&function)) {
return false;
}
int function_index = function->function_index();
FunctionSig* signature = instance->module()->functions[function_index].sig;
size_t param_count = signature->parameter_count();
std::unique_ptr<WasmValue[]> arguments(new WasmValue[param_count]);
size_t arg_count = std::min(param_count, argc);
if (arg_count > 0) {
memcpy(arguments.get(), args, arg_count);
}
// Fill the parameters up with default values.
for (size_t i = argc; i < param_count; ++i) {
switch (signature->GetParam(i)) {
case kWasmI32:
arguments[i] = WasmValue(int32_t{0});
break;
case kWasmI64:
arguments[i] = WasmValue(int64_t{0});
break;
case kWasmF32:
arguments[i] = WasmValue(0.0f);
break;
case kWasmF64:
arguments[i] = WasmValue(0.0);
break;
default:
UNREACHABLE();
}
}
// Don't execute more than 16k steps.
constexpr int kMaxNumSteps = 16 * 1024;
Zone zone(isolate->allocator(), ZONE_NAME);
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
thread->Reset();
// Start an activation so that we can deal with stack overflows. We do not
// finish the activation. An activation is just part of the state of the
// interpreter, and we do not reuse the interpreter anyways. In addition,
// finishing the activation is not correct in all cases, e.g. when the
// execution of the interpreter did not finish after kMaxNumSteps.
thread->StartActivation();
thread->InitFrame(&instance->module()->functions[function_index],
arguments.get());
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
if (isolate->has_pending_exception()) {
// Stack overflow during interpretation.
isolate->clear_pending_exception();
return false;
}
return interpreter_result != WasmInterpreter::PAUSED;
}
int32_t RunWasmModuleForTesting(Isolate* isolate,
Handle<WasmInstanceObject> instance, int argc,
Handle<Object> argv[]) {
ErrorThrower thrower(isolate, "RunWasmModule");
return CallWasmFunctionForTesting(isolate, instance, &thrower, "main", argc,
argv);
}
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end) {
HandleScope scope(isolate);
ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
MaybeHandle<WasmInstanceObject> instance = CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(module_start, module_end));
if (instance.is_null()) {
return -1;
}
return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
nullptr);
}
int32_t CompileAndRunAsmWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end) {
HandleScope scope(isolate);
ErrorThrower thrower(isolate, "CompileAndRunAsmWasmModule");
MaybeHandle<AsmWasmData> data =
isolate->wasm_engine()->SyncCompileTranslatedAsmJs(
isolate, &thrower, ModuleWireBytes(module_start, module_end),
Vector<const byte>(), Handle<HeapNumber>(), LanguageMode::kSloppy);
DCHECK_EQ(thrower.error(), data.is_null());
if (data.is_null()) return -1;
MaybeHandle<WasmModuleObject> module =
isolate->wasm_engine()->FinalizeTranslatedAsmJs(
isolate, data.ToHandleChecked(), Handle<Script>::null());
MaybeHandle<WasmInstanceObject> instance =
isolate->wasm_engine()->SyncInstantiate(
isolate, &thrower, module.ToHandleChecked(),
Handle<JSReceiver>::null(), Handle<JSArrayBuffer>::null());
DCHECK_EQ(thrower.error(), instance.is_null());
if (instance.is_null()) return -1;
return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
nullptr);
}
WasmInterpretationResult InterpretWasmModule(
Isolate* isolate, Handle<WasmInstanceObject> instance,
int32_t function_index, WasmValue* args) {
// Don't execute more than 16k steps.
constexpr int kMaxNumSteps = 16 * 1024;
Zone zone(isolate->allocator(), ZONE_NAME);
v8::internal::HandleScope scope(isolate);
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
thread->Reset();
// Start an activation so that we can deal with stack overflows. We do not
// finish the activation. An activation is just part of the state of the
// interpreter, and we do not reuse the interpreter anyways. In addition,
// finishing the activation is not correct in all cases, e.g. when the
// execution of the interpreter did not finish after kMaxNumSteps.
thread->StartActivation();
thread->InitFrame(&(instance->module()->functions[function_index]), args);
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
bool stack_overflow = isolate->has_pending_exception();
isolate->clear_pending_exception();
if (stack_overflow) return WasmInterpretationResult::Stopped();
if (thread->state() == WasmInterpreter::TRAPPED) {
return WasmInterpretationResult::Trapped(thread->PossibleNondeterminism());
}
if (interpreter_result == WasmInterpreter::FINISHED) {
return WasmInterpretationResult::Finished(
thread->GetReturnValue().to<int32_t>(),
thread->PossibleNondeterminism());
}
return WasmInterpretationResult::Stopped();
}
MaybeHandle<WasmExportedFunction> GetExportedFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, const char* name) {
Handle<JSObject> exports_object;
Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
exports_object = Handle<JSObject>::cast(
JSObject::GetProperty(isolate, instance, exports).ToHandleChecked());
Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name);
PropertyDescriptor desc;
Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
isolate, exports_object, main_name, &desc);
if (!property_found.FromMaybe(false)) return {};
if (!desc.value()->IsJSFunction()) return {};
return Handle<WasmExportedFunction>::cast(desc.value());
}
int32_t CallWasmFunctionForTesting(Isolate* isolate,
Handle<WasmInstanceObject> instance,
ErrorThrower* thrower, const char* name,
int argc, Handle<Object> argv[]) {
MaybeHandle<WasmExportedFunction> maybe_export =
GetExportedFunction(isolate, instance, name);
Handle<WasmExportedFunction> main_export;
if (!maybe_export.ToHandle(&main_export)) {
return -1;
}
// Call the JS function.
Handle<Object> undefined = isolate->factory()->undefined_value();
MaybeHandle<Object> retval =
Execution::Call(isolate, main_export, undefined, argc, argv);
// The result should be a number.
if (retval.is_null()) {
DCHECK(isolate->has_pending_exception());
isolate->clear_pending_exception();
thrower->RuntimeError("Calling exported wasm function failed.");
return -1;
}
Handle<Object> result = retval.ToHandleChecked();
if (result->IsSmi()) {
return Smi::ToInt(*result);
}
if (result->IsHeapNumber()) {
return static_cast<int32_t>(HeapNumber::cast(*result).value());
}
thrower->RuntimeError(
"Calling exported wasm function failed: Return value should be number");
return -1;
}
void SetupIsolateForWasmModule(Isolate* isolate) {
WasmJs::Install(isolate, true);
}
} // namespace testing
} // namespace wasm
} // namespace internal
} // namespace v8