| Index: test/cctest/wasm/wasm-module-runner.cc
|
| diff --git a/test/cctest/wasm/wasm-module-runner.cc b/test/cctest/wasm/wasm-module-runner.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..55bebede4eebf7747434ef721e79b6c17afc36a6
|
| --- /dev/null
|
| +++ b/test/cctest/wasm/wasm-module-runner.cc
|
| @@ -0,0 +1,194 @@
|
| +// 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/cctest/wasm/wasm-module-runner.h"
|
| +
|
| +#include "src/handles.h"
|
| +#include "src/isolate.h"
|
| +#include "src/objects.h"
|
| +#include "src/property-descriptor.h"
|
| +#include "src/wasm/module-decoder.h"
|
| +#include "src/wasm/wasm-interpreter.h"
|
| +#include "src/wasm/wasm-module.h"
|
| +#include "src/wasm/wasm-result.h"
|
| +#include "src/zone.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +namespace wasm {
|
| +namespace testing {
|
| +
|
| +uint32_t GetMinModuleMemSize(const WasmModule* module) {
|
| + return WasmModule::kPageSize * module->min_mem_pages;
|
| +}
|
| +
|
| +const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone,
|
| + ErrorThrower& thrower,
|
| + const byte* module_start,
|
| + const byte* module_end,
|
| + ModuleOrigin origin) {
|
| + // Decode the module, but don't verify function bodies, since we'll
|
| + // be compiling them anyway.
|
| + ModuleResult decoding_result =
|
| + DecodeWasmModule(isolate, zone, module_start, module_end, false, origin);
|
| +
|
| + std::unique_ptr<const WasmModule> module(decoding_result.val);
|
| + if (decoding_result.failed()) {
|
| + // Module verification failed. throw.
|
| + thrower.Error("WASM.compileRun() failed: %s",
|
| + decoding_result.error_msg.get());
|
| + return nullptr;
|
| + }
|
| +
|
| + if (thrower.error()) return nullptr;
|
| + return module.release();
|
| +}
|
| +
|
| +const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
|
| + ErrorThrower& thrower,
|
| + const WasmModule* module) {
|
| + CHECK(module != nullptr);
|
| +
|
| + if (module->import_table.size() > 0) {
|
| + thrower.Error("Not supported: module has imports.");
|
| + }
|
| + if (module->export_table.size() == 0) {
|
| + thrower.Error("Not supported: module has no exports.");
|
| + }
|
| +
|
| + if (thrower.error()) return Handle<JSObject>::null();
|
| +
|
| + MaybeHandle<FixedArray> compiled_module =
|
| + module->CompileFunctions(isolate, &thrower);
|
| +
|
| + if (compiled_module.is_null()) return Handle<JSObject>::null();
|
| + return WasmModule::Instantiate(isolate, compiled_module.ToHandleChecked(),
|
| + Handle<JSReceiver>::null(),
|
| + Handle<JSArrayBuffer>::null())
|
| + .ToHandleChecked();
|
| +}
|
| +
|
| +int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
|
| + const byte* module_end, bool asm_js) {
|
| + HandleScope scope(isolate);
|
| + Zone zone(isolate->allocator());
|
| +
|
| + ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
|
| + std::unique_ptr<const WasmModule> module(DecodeWasmModuleForTesting(
|
| + isolate, &zone, thrower, module_start, module_end,
|
| + asm_js ? kAsmJsOrigin : kWasmOrigin));
|
| +
|
| + if (module == nullptr) {
|
| + return -1;
|
| + }
|
| + Handle<JSObject> instance =
|
| + InstantiateModuleForTesting(isolate, thrower, module.get());
|
| + if (instance.is_null()) {
|
| + return -1;
|
| + }
|
| + return CallWasmFunctionForTesting(isolate, instance, thrower,
|
| + asm_js ? "caller" : "main", 0, nullptr,
|
| + asm_js);
|
| +}
|
| +
|
| +int32_t InterpretWasmModule(Isolate* isolate, ErrorThrower& thrower,
|
| + const WasmModule* module, int function_index,
|
| + WasmVal* args) {
|
| + CHECK(module != nullptr);
|
| +
|
| + Zone zone(isolate->allocator());
|
| + v8::internal::HandleScope scope(isolate);
|
| +
|
| + if (module->import_table.size() > 0) {
|
| + thrower.Error("Not supported: module has imports.");
|
| + }
|
| + if (module->export_table.size() == 0) {
|
| + thrower.Error("Not supported: module has no exports.");
|
| + }
|
| +
|
| + if (thrower.error()) return -1;
|
| +
|
| + WasmModuleInstance instance(module);
|
| + instance.context = isolate->native_context();
|
| + instance.mem_size = GetMinModuleMemSize(module);
|
| + instance.mem_start = nullptr;
|
| + instance.globals_start = nullptr;
|
| +
|
| + ModuleEnv module_env;
|
| + module_env.module = module;
|
| + module_env.instance = &instance;
|
| + module_env.origin = module->origin;
|
| +
|
| + const WasmFunction* function = &(module->functions[function_index]);
|
| +
|
| + FunctionBody body = {&module_env, function->sig, module->module_start,
|
| + module->module_start + function->code_start_offset,
|
| + module->module_start + function->code_end_offset};
|
| + DecodeResult result = VerifyWasmCode(isolate->allocator(), body);
|
| + if (result.failed()) {
|
| + thrower.Error("Function did not verify");
|
| + return -1;
|
| + }
|
| +
|
| + WasmInterpreter interpreter(&instance, isolate->allocator());
|
| +
|
| + WasmInterpreter::Thread* thread = interpreter.GetThread(0);
|
| + thread->Reset();
|
| + thread->PushFrame(function, args);
|
| + if (thread->Run() == WasmInterpreter::FINISHED) {
|
| + WasmVal val = thread->GetReturnValue();
|
| + return val.to<int32_t>();
|
| + } else if (thread->state() == WasmInterpreter::TRAPPED) {
|
| + return 0xdeadbeef;
|
| + } else {
|
| + thrower.Error("Interpreter did not finish execution within its step bound");
|
| + return -1;
|
| + }
|
| +}
|
| +
|
| +int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle<JSObject> instance,
|
| + ErrorThrower& thrower, const char* name,
|
| + int argc, Handle<Object> argv[],
|
| + bool asm_js) {
|
| + Handle<JSObject> exports_object;
|
| + if (asm_js) {
|
| + exports_object = instance;
|
| + } else {
|
| + Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
|
| + exports_object = Handle<JSObject>::cast(
|
| + JSObject::GetProperty(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 -1;
|
| +
|
| + Handle<JSFunction> main_export = Handle<JSFunction>::cast(desc.value());
|
| +
|
| + // 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()) {
|
| + thrower.Error("WASM.compileRun() failed: Invocation was null");
|
| + return -1;
|
| + }
|
| + Handle<Object> result = retval.ToHandleChecked();
|
| + if (result->IsSmi()) {
|
| + return Smi::cast(*result)->value();
|
| + }
|
| + if (result->IsHeapNumber()) {
|
| + return static_cast<int32_t>(HeapNumber::cast(*result)->value());
|
| + }
|
| + thrower.Error("WASM.compileRun() failed: Return value should be number");
|
| + return -1;
|
| +}
|
| +
|
| +} // namespace testing
|
| +} // namespace wasm
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|