2016-09-12 12:26:37 +00:00
|
|
|
// 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.
|
|
|
|
|
2016-09-14 10:31:23 +00:00
|
|
|
#include "test/common/wasm/wasm-module-runner.h"
|
2016-09-12 12:26:37 +00:00
|
|
|
|
|
|
|
#include "src/handles.h"
|
|
|
|
#include "src/isolate.h"
|
2017-02-13 09:52:26 +00:00
|
|
|
#include "src/objects-inl.h"
|
2016-09-12 12:26:37 +00:00
|
|
|
#include "src/objects.h"
|
|
|
|
#include "src/property-descriptor.h"
|
|
|
|
#include "src/wasm/module-decoder.h"
|
|
|
|
#include "src/wasm/wasm-interpreter.h"
|
2016-09-17 01:30:09 +00:00
|
|
|
#include "src/wasm/wasm-js.h"
|
2016-09-12 12:26:37 +00:00
|
|
|
#include "src/wasm/wasm-module.h"
|
2016-11-11 11:12:31 +00:00
|
|
|
#include "src/wasm/wasm-objects.h"
|
2016-09-12 12:26:37 +00:00
|
|
|
#include "src/wasm/wasm-result.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace wasm {
|
|
|
|
namespace testing {
|
|
|
|
|
|
|
|
uint32_t GetMinModuleMemSize(const WasmModule* module) {
|
|
|
|
return WasmModule::kPageSize * module->min_mem_pages;
|
|
|
|
}
|
|
|
|
|
2016-11-14 19:45:16 +00:00
|
|
|
const WasmModule* DecodeWasmModuleForTesting(
|
|
|
|
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
|
|
|
|
const byte* module_end, ModuleOrigin origin, bool verify_functions) {
|
2016-09-12 12:26:37 +00:00
|
|
|
// Decode the module, but don't verify function bodies, since we'll
|
|
|
|
// be compiling them anyway.
|
2016-11-14 19:45:16 +00:00
|
|
|
ModuleResult decoding_result = DecodeWasmModule(
|
|
|
|
isolate, module_start, module_end, verify_functions, origin);
|
2016-09-12 12:26:37 +00:00
|
|
|
|
|
|
|
if (decoding_result.failed()) {
|
|
|
|
// Module verification failed. throw.
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->CompileError("WASM.compileRun() failed: %s",
|
2017-04-10 10:48:48 +00:00
|
|
|
decoding_result.error_msg.c_str());
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
|
[wasm] Use a Managed<WasmModule> to hold metadata about modules.
This CL refactors the handling of metadata associated with WebAssembly
modules to reduce the duplicate marshalling of data from the C++ world
to the JavaScript world. It does this by wrapping the C++ WasmModule*
object in a Foreign that is rooted from the on-heap WasmCompiledModule
(which is itself just a FixedArray). Upon serialization, the C++ object
is ignored and the original WASM wire bytes are serialized. Upon
deserialization, the C++ object is reconstituted by reparsing the bytes.
This is motivated by increasing complications in implementing the JS
API, in particular WebAssembly.Table, which must perform signature
canonicalization across instances.
Additionally, this CL implements the proper base + offset initialization
behavior for tables.
R=rossberg@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org,yangguo@chromium.org
BUG=v8:5507, chromium:575167, chromium:657316
Review-Url: https://chromiumcodereview.appspot.com/2424623002
Cr-Commit-Position: refs/heads/master@{#40434}
2016-10-19 13:06:44 +00:00
|
|
|
if (thrower->error()) {
|
|
|
|
if (decoding_result.val) delete decoding_result.val;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return decoding_result.val;
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
|
2016-11-16 16:17:22 +00:00
|
|
|
const Handle<WasmInstanceObject> InstantiateModuleForTesting(
|
2016-11-30 15:02:40 +00:00
|
|
|
Isolate* isolate, ErrorThrower* thrower, const WasmModule* module,
|
|
|
|
const ModuleWireBytes& wire_bytes) {
|
|
|
|
DCHECK_NOT_NULL(module);
|
2016-09-12 12:26:37 +00:00
|
|
|
if (module->import_table.size() > 0) {
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->CompileError("Not supported: module has imports.");
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
2016-11-09 11:38:30 +00:00
|
|
|
|
2016-11-16 16:17:22 +00:00
|
|
|
if (thrower->error()) return Handle<WasmInstanceObject>::null();
|
2016-09-12 12:26:37 +00:00
|
|
|
|
2016-09-12 22:11:12 +00:00
|
|
|
// Although we decoded the module for some pre-validation, run the bytes
|
|
|
|
// again through the normal pipeline.
|
2016-10-26 12:08:32 +00:00
|
|
|
// TODO(wasm): Use {module} instead of decoding the module bytes again.
|
2017-02-20 10:41:57 +00:00
|
|
|
MaybeHandle<WasmModuleObject> module_object =
|
|
|
|
SyncCompile(isolate, thrower, wire_bytes);
|
2016-09-28 20:55:42 +00:00
|
|
|
if (module_object.is_null()) {
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->CompileError("Module pre-validation failed.");
|
2016-11-16 16:17:22 +00:00
|
|
|
return Handle<WasmInstanceObject>::null();
|
2016-09-28 20:55:42 +00:00
|
|
|
}
|
2017-01-16 08:58:25 +00:00
|
|
|
MaybeHandle<WasmInstanceObject> maybe_instance =
|
2017-02-20 10:41:57 +00:00
|
|
|
SyncInstantiate(isolate, thrower, module_object.ToHandleChecked(),
|
|
|
|
Handle<JSReceiver>::null(), MaybeHandle<JSArrayBuffer>());
|
2016-11-16 16:17:22 +00:00
|
|
|
Handle<WasmInstanceObject> instance;
|
2016-09-22 14:23:32 +00:00
|
|
|
if (!maybe_instance.ToHandle(&instance)) {
|
2016-11-16 16:17:22 +00:00
|
|
|
return Handle<WasmInstanceObject>::null();
|
2016-09-22 14:23:32 +00:00
|
|
|
}
|
|
|
|
return instance;
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
|
2016-11-16 16:17:22 +00:00
|
|
|
const Handle<WasmInstanceObject> CompileInstantiateWasmModuleForTesting(
|
[wasm] Use a Managed<WasmModule> to hold metadata about modules.
This CL refactors the handling of metadata associated with WebAssembly
modules to reduce the duplicate marshalling of data from the C++ world
to the JavaScript world. It does this by wrapping the C++ WasmModule*
object in a Foreign that is rooted from the on-heap WasmCompiledModule
(which is itself just a FixedArray). Upon serialization, the C++ object
is ignored and the original WASM wire bytes are serialized. Upon
deserialization, the C++ object is reconstituted by reparsing the bytes.
This is motivated by increasing complications in implementing the JS
API, in particular WebAssembly.Table, which must perform signature
canonicalization across instances.
Additionally, this CL implements the proper base + offset initialization
behavior for tables.
R=rossberg@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org,yangguo@chromium.org
BUG=v8:5507, chromium:575167, chromium:657316
Review-Url: https://chromiumcodereview.appspot.com/2424623002
Cr-Commit-Position: refs/heads/master@{#40434}
2016-10-19 13:06:44 +00:00
|
|
|
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
|
|
|
|
const byte* module_end, ModuleOrigin origin) {
|
2016-10-26 12:08:32 +00:00
|
|
|
std::unique_ptr<const WasmModule> module(DecodeWasmModuleForTesting(
|
|
|
|
isolate, thrower, module_start, module_end, origin));
|
2016-09-28 20:55:42 +00:00
|
|
|
|
|
|
|
if (module == nullptr) {
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->CompileError("Wasm module decoding failed");
|
2016-11-16 16:17:22 +00:00
|
|
|
return Handle<WasmInstanceObject>::null();
|
2016-09-28 20:55:42 +00:00
|
|
|
}
|
2016-11-30 15:02:40 +00:00
|
|
|
return InstantiateModuleForTesting(isolate, thrower, module.get(),
|
|
|
|
ModuleWireBytes(module_start, module_end));
|
2016-09-28 20:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance,
|
|
|
|
int argc, Handle<Object> argv[],
|
|
|
|
ModuleOrigin origin) {
|
|
|
|
ErrorThrower thrower(isolate, "RunWasmModule");
|
|
|
|
const char* f_name = origin == ModuleOrigin::kAsmJsOrigin ? "caller" : "main";
|
|
|
|
return CallWasmFunctionForTesting(isolate, instance, &thrower, f_name, argc,
|
|
|
|
argv, origin);
|
|
|
|
}
|
|
|
|
|
2016-09-12 12:26:37 +00:00
|
|
|
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
|
2016-09-12 22:11:12 +00:00
|
|
|
const byte* module_end, ModuleOrigin origin) {
|
2016-09-12 12:26:37 +00:00
|
|
|
HandleScope scope(isolate);
|
2016-10-13 10:56:48 +00:00
|
|
|
ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
|
2016-09-28 20:55:42 +00:00
|
|
|
Handle<JSObject> instance = CompileInstantiateWasmModuleForTesting(
|
[wasm] Use a Managed<WasmModule> to hold metadata about modules.
This CL refactors the handling of metadata associated with WebAssembly
modules to reduce the duplicate marshalling of data from the C++ world
to the JavaScript world. It does this by wrapping the C++ WasmModule*
object in a Foreign that is rooted from the on-heap WasmCompiledModule
(which is itself just a FixedArray). Upon serialization, the C++ object
is ignored and the original WASM wire bytes are serialized. Upon
deserialization, the C++ object is reconstituted by reparsing the bytes.
This is motivated by increasing complications in implementing the JS
API, in particular WebAssembly.Table, which must perform signature
canonicalization across instances.
Additionally, this CL implements the proper base + offset initialization
behavior for tables.
R=rossberg@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org,yangguo@chromium.org
BUG=v8:5507, chromium:575167, chromium:657316
Review-Url: https://chromiumcodereview.appspot.com/2424623002
Cr-Commit-Position: refs/heads/master@{#40434}
2016-10-19 13:06:44 +00:00
|
|
|
isolate, &thrower, module_start, module_end, origin);
|
2016-09-12 12:26:37 +00:00
|
|
|
if (instance.is_null()) {
|
|
|
|
return -1;
|
|
|
|
}
|
2016-09-28 20:55:42 +00:00
|
|
|
return RunWasmModuleForTesting(isolate, instance, 0, nullptr, origin);
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
|
2016-09-15 16:19:48 +00:00
|
|
|
int32_t InterpretWasmModule(Isolate* isolate, ErrorThrower* thrower,
|
2016-11-30 15:02:40 +00:00
|
|
|
const WasmModule* module,
|
|
|
|
const ModuleWireBytes& wire_bytes,
|
|
|
|
int function_index, WasmVal* args,
|
|
|
|
bool* possible_nondeterminism) {
|
2017-04-06 10:57:24 +00:00
|
|
|
// Don't execute more than 16k steps.
|
|
|
|
constexpr int kMaxNumSteps = 16 * 1024;
|
|
|
|
|
2016-11-30 15:02:40 +00:00
|
|
|
DCHECK_NOT_NULL(module);
|
2016-10-17 12:12:30 +00:00
|
|
|
Zone zone(isolate->allocator(), ZONE_NAME);
|
2016-09-12 12:26:37 +00:00
|
|
|
v8::internal::HandleScope scope(isolate);
|
|
|
|
|
|
|
|
if (module->import_table.size() > 0) {
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->CompileError("Not supported: module has imports.");
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
if (module->export_table.size() == 0) {
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->CompileError("Not supported: module has no exports.");
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
|
2016-09-15 16:19:48 +00:00
|
|
|
if (thrower->error()) return -1;
|
2016-09-12 12:26:37 +00:00
|
|
|
|
2016-09-14 12:55:43 +00:00
|
|
|
// The code verifies, we create an instance to run it in the interpreter.
|
2016-10-12 13:57:03 +00:00
|
|
|
WasmInstance instance(module);
|
2016-09-14 12:55:43 +00:00
|
|
|
instance.context = isolate->native_context();
|
|
|
|
instance.mem_size = GetMinModuleMemSize(module);
|
|
|
|
// TODO(ahaas): Move memory allocation to wasm-module.cc for better
|
|
|
|
// encapsulation.
|
|
|
|
instance.mem_start =
|
|
|
|
static_cast<byte*>(calloc(GetMinModuleMemSize(module), 1));
|
|
|
|
instance.globals_start = nullptr;
|
|
|
|
|
2016-11-30 15:02:40 +00:00
|
|
|
ModuleBytesEnv env(module, &instance, wire_bytes);
|
2017-03-23 09:46:16 +00:00
|
|
|
WasmInterpreter interpreter(isolate, env);
|
2016-09-12 12:26:37 +00:00
|
|
|
|
|
|
|
WasmInterpreter::Thread* thread = interpreter.GetThread(0);
|
|
|
|
thread->Reset();
|
2017-03-14 15:54:43 +00:00
|
|
|
thread->InitFrame(&(module->functions[function_index]), args);
|
2017-04-06 10:57:24 +00:00
|
|
|
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
|
2016-09-14 12:55:43 +00:00
|
|
|
if (instance.mem_start) {
|
|
|
|
free(instance.mem_start);
|
|
|
|
}
|
2016-10-20 14:27:23 +00:00
|
|
|
*possible_nondeterminism = thread->PossibleNondeterminism();
|
2016-09-14 12:55:43 +00:00
|
|
|
if (interpreter_result == WasmInterpreter::FINISHED) {
|
2016-09-12 12:26:37 +00:00
|
|
|
WasmVal val = thread->GetReturnValue();
|
|
|
|
return val.to<int32_t>();
|
|
|
|
} else if (thread->state() == WasmInterpreter::TRAPPED) {
|
|
|
|
return 0xdeadbeef;
|
|
|
|
} else {
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->RangeError(
|
2016-09-15 16:19:48 +00:00
|
|
|
"Interpreter did not finish execution within its step bound");
|
2016-09-12 12:26:37 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle<JSObject> instance,
|
2016-09-15 16:19:48 +00:00
|
|
|
ErrorThrower* thrower, const char* name,
|
2016-09-12 12:26:37 +00:00
|
|
|
int argc, Handle<Object> argv[],
|
2016-09-12 22:11:12 +00:00
|
|
|
ModuleOrigin origin) {
|
2016-09-12 12:26:37 +00:00
|
|
|
Handle<JSObject> exports_object;
|
2016-09-12 22:11:12 +00:00
|
|
|
if (origin == ModuleOrigin::kAsmJsOrigin) {
|
2016-09-12 12:26:37 +00:00
|
|
|
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()) {
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->RuntimeError("WASM.compileRun() failed: Invocation was null");
|
2016-09-12 12:26:37 +00:00
|
|
|
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());
|
|
|
|
}
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->RuntimeError(
|
|
|
|
"WASM.compileRun() failed: Return value should be number");
|
2016-09-12 12:26:37 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-09-17 01:30:09 +00:00
|
|
|
void SetupIsolateForWasmModule(Isolate* isolate) {
|
2017-01-12 20:32:27 +00:00
|
|
|
WasmJs::Install(isolate);
|
2016-09-17 01:30:09 +00:00
|
|
|
}
|
2016-09-12 12:26:37 +00:00
|
|
|
} // namespace testing
|
|
|
|
} // namespace wasm
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|