v8/test/common/wasm/wasm-module-runner.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

235 lines
8.3 KiB
C++
Raw Normal View History

// 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-js.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
#include "test/common/wasm/wasm-interpreter.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace testing {
MaybeHandle<WasmModuleObject> CompileForTesting(Isolate* isolate,
ErrorThrower* thrower,
const ModuleWireBytes& bytes) {
auto enabled_features = WasmFeatures::FromIsolate(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(), {}, {});
}
WasmInterpretationResult GetInterpretationResult(
Isolate* isolate, const WasmInterpreter& interpreter,
WasmInterpreter::State interpreter_result) {
bool stack_overflow = isolate->has_pending_exception();
isolate->clear_pending_exception();
if (stack_overflow) return WasmInterpretationResult::Failed();
if (interpreter.state() == WasmInterpreter::TRAPPED) {
return WasmInterpretationResult::Trapped(
interpreter.PossibleNondeterminism());
}
if (interpreter_result == WasmInterpreter::FINISHED) {
return WasmInterpretationResult::Finished(
interpreter.GetReturnValue().to<int32_t>(),
interpreter.PossibleNondeterminism());
}
// The interpreter did not finish within the limited number of steps, so it
// might execute an infinite loop or infinite recursion. Return "failed"
// status in that case.
return WasmInterpretationResult::Failed();
}
WasmInterpretationResult InterpretWasmModuleForTesting(
Isolate* isolate, Handle<WasmInstanceObject> instance, size_t argc,
WasmValue* args) {
HandleScope handle_scope(isolate); // Avoid leaking handles.
WasmCodeRefScope code_ref_scope;
Handle<WasmExportedFunction> function;
if (!GetExportedFunction(isolate, instance, "main").ToHandle(&function)) {
return WasmInterpretationResult::Failed();
}
int function_index = function->function_index();
const 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).kind()) {
case ValueType::kI32:
arguments[i] = WasmValue(int32_t{0});
break;
case ValueType::kI64:
arguments[i] = WasmValue(int64_t{0});
break;
case ValueType::kF32:
arguments[i] = WasmValue(0.0f);
break;
case ValueType::kF64:
arguments[i] = WasmValue(0.0);
break;
case ValueType::kOptRef:
arguments[i] =
WasmValue(Handle<Object>::cast(isolate->factory()->null_value()));
break;
[wasm-gc] Change ValueType representation to account for new types Motivation: Changes to the typed function references and gc proposals solidified the notion of heap type, clarified nullable vs. non-nullable reference types, and introduced rtts, which contain an integer depth field in addition to a heap type. This required us to overhaul our ValueType representation, which results in extensive changes. To keep this CL "small", we do not try to implement the binary encoding as described in the proposals, but rather devise a simpler one of our own (see below). Also, we do not try to implement additional functionality for the new types. Changes: - Introduce HeapType. Move heap types from ValueType to HeapType. - Introduce Nullability for reference types. - Rework ValueType helper methods. - Introduce rtts in ValueType with an integer depth field. Include depth in the ValueType encoding. - Make the constructor of ValueType private, instead expose static functions which explicitly state what they create. - Change every switch statement on ValueType::Kind. Sometimes, we need nested switches. - Introduce temporary constants in ValueTypeCode for nullable types, use them for decoding. - In WasmGlobalObject, split 'flags' into 'raw_type' and 'is_mutable'. - Change IsSubtypeOfRef to IsSubtypeOfHeap and implement changes in subtyping. - kWasmFuncRef initializers are now non-nullable. Initializers are only required to be subtypes of the declared global type. - Change tests and fuzzers as needed. Bug: v8:7748 Change-Id: If41f783bd4128443b07e94188cea7dd53ab0bfa5 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2247657 Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#68408}
2020-06-18 11:24:07 +00:00
case ValueType::kRef:
case ValueType::kRtt:
case ValueType::kI8:
case ValueType::kI16:
case ValueType::kStmt:
case ValueType::kBottom:
case ValueType::kS128:
UNREACHABLE();
}
}
// Don't execute more than 16k steps.
constexpr int kMaxNumSteps = 16 * 1024;
Zone zone(isolate->allocator(), ZONE_NAME);
WasmInterpreter interpreter{
isolate, instance->module(),
ModuleWireBytes{instance->module_object().native_module()->wire_bytes()},
instance};
interpreter.InitFrame(&instance->module()->functions[function_index],
arguments.get());
WasmInterpreter::State interpreter_result = interpreter.Run(kMaxNumSteps);
return GetInterpretationResult(isolate, interpreter, interpreter_result);
}
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);
}
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{
isolate, instance->module(),
ModuleWireBytes{instance->module_object().native_module()->wire_bytes()},
instance};
interpreter.InitFrame(&instance->module()->functions[function_index], args);
WasmInterpreter::State interpreter_result = interpreter.Run(kMaxNumSteps);
return GetInterpretationResult(isolate, interpreter, interpreter_result);
}
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