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"
|
2017-09-28 11:02:36 +00:00
|
|
|
#include "src/wasm/module-compiler.h"
|
2016-09-12 12:26:37 +00:00
|
|
|
#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 {
|
|
|
|
|
2017-08-10 12:45:45 +00:00
|
|
|
uint32_t GetInitialMemSize(const WasmModule* module) {
|
2018-01-11 11:49:29 +00:00
|
|
|
return kWasmPageSize * module->initial_pages;
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
|
2017-05-08 12:45:20 +00:00
|
|
|
std::unique_ptr<WasmModule> DecodeWasmModuleForTesting(
|
2016-11-14 19:45:16 +00:00
|
|
|
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.
|
2017-06-27 18:50:38 +00:00
|
|
|
ModuleResult decoding_result = SyncDecodeWasmModule(
|
2016-11-14 19:45:16 +00:00
|
|
|
isolate, module_start, module_end, verify_functions, origin);
|
2016-09-12 12:26:37 +00:00
|
|
|
|
|
|
|
if (decoding_result.failed()) {
|
|
|
|
// Module verification failed. throw.
|
2017-06-09 12:21:52 +00:00
|
|
|
thrower->CompileError("DecodeWasmModule failed: %s",
|
2017-04-28 11:21:00 +00:00
|
|
|
decoding_result.error_msg().c_str());
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
|
2017-05-08 12:45:20 +00:00
|
|
|
return std::move(decoding_result.val);
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
|
2017-09-07 09:48:34 +00:00
|
|
|
bool InterpretWasmModuleForTesting(Isolate* isolate,
|
|
|
|
Handle<WasmInstanceObject> instance,
|
|
|
|
const char* name, size_t argc,
|
|
|
|
WasmValue* args) {
|
|
|
|
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]);
|
|
|
|
|
|
|
|
memcpy(arguments.get(), args, std::min(param_count, argc));
|
|
|
|
|
|
|
|
// Fill the parameters up with default values.
|
|
|
|
for (size_t i = argc; i < param_count; ++i) {
|
|
|
|
switch (signature->GetParam(i)) {
|
|
|
|
case MachineRepresentation::kWord32:
|
|
|
|
arguments[i] = WasmValue(int32_t{0});
|
|
|
|
break;
|
|
|
|
case MachineRepresentation::kWord64:
|
|
|
|
arguments[i] = WasmValue(int64_t{0});
|
|
|
|
break;
|
|
|
|
case MachineRepresentation::kFloat32:
|
|
|
|
arguments[i] = WasmValue(0.0f);
|
|
|
|
break;
|
|
|
|
case MachineRepresentation::kFloat64:
|
|
|
|
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::HeapObjectsScope heap_objects_scope(interpreter, instance);
|
|
|
|
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
|
|
|
thread->Reset();
|
2017-11-21 14:17:00 +00:00
|
|
|
|
|
|
|
// 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();
|
2017-09-08 15:02:28 +00:00
|
|
|
thread->InitFrame(&instance->module()->functions[function_index],
|
|
|
|
arguments.get());
|
2017-09-07 09:48:34 +00:00
|
|
|
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
|
|
|
|
|
2017-11-21 14:17:00 +00:00
|
|
|
isolate->clear_pending_exception();
|
|
|
|
|
2017-09-07 09:48:34 +00:00
|
|
|
return interpreter_result != WasmInterpreter::PAUSED;
|
|
|
|
}
|
|
|
|
|
2017-10-11 12:52:26 +00:00
|
|
|
int32_t RunWasmModuleForTesting(Isolate* isolate,
|
|
|
|
Handle<WasmInstanceObject> instance, int argc,
|
|
|
|
Handle<Object> argv[]) {
|
2016-09-28 20:55:42 +00:00
|
|
|
ErrorThrower thrower(isolate, "RunWasmModule");
|
2017-06-21 09:24:03 +00:00
|
|
|
return CallWasmFunctionForTesting(isolate, instance, &thrower, "main", argc,
|
|
|
|
argv);
|
2016-09-28 20:55:42 +00:00
|
|
|
}
|
|
|
|
|
2016-09-12 12:26:37 +00:00
|
|
|
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
|
2017-06-09 18:37:54 +00:00
|
|
|
const byte* module_end) {
|
2016-09-12 12:26:37 +00:00
|
|
|
HandleScope scope(isolate);
|
2016-10-13 10:56:48 +00:00
|
|
|
ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
|
2017-06-09 18:37:54 +00:00
|
|
|
MaybeHandle<WasmInstanceObject> instance = SyncCompileAndInstantiate(
|
|
|
|
isolate, &thrower, ModuleWireBytes(module_start, module_end), {}, {});
|
2016-09-12 12:26:37 +00:00
|
|
|
if (instance.is_null()) {
|
|
|
|
return -1;
|
|
|
|
}
|
2017-06-09 18:37:54 +00:00
|
|
|
return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
|
2017-06-21 09:24:03 +00:00
|
|
|
nullptr);
|
2017-06-09 18:37:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int32_t CompileAndRunAsmWasmModule(Isolate* isolate, const byte* module_start,
|
|
|
|
const byte* module_end) {
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
ErrorThrower thrower(isolate, "CompileAndRunAsmWasmModule");
|
|
|
|
MaybeHandle<WasmModuleObject> module = wasm::SyncCompileTranslatedAsmJs(
|
|
|
|
isolate, &thrower, ModuleWireBytes(module_start, module_end),
|
|
|
|
Handle<Script>::null(), Vector<const byte>());
|
|
|
|
DCHECK_EQ(thrower.error(), module.is_null());
|
|
|
|
if (module.is_null()) return -1;
|
|
|
|
|
|
|
|
MaybeHandle<WasmInstanceObject> instance = wasm::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,
|
2017-06-21 09:24:03 +00:00
|
|
|
nullptr);
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
2017-06-12 11:13:15 +00:00
|
|
|
int32_t InterpretWasmModule(Isolate* isolate,
|
|
|
|
Handle<WasmInstanceObject> instance,
|
|
|
|
ErrorThrower* thrower, int32_t function_index,
|
2017-07-14 13:49:01 +00:00
|
|
|
WasmValue* 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-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);
|
|
|
|
|
2017-08-07 17:17:06 +00:00
|
|
|
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
|
2017-07-14 13:58:25 +00:00
|
|
|
WasmInterpreter::HeapObjectsScope heap_objects_scope(interpreter, instance);
|
2017-06-12 11:13:15 +00:00
|
|
|
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
2016-09-12 12:26:37 +00:00
|
|
|
thread->Reset();
|
2017-11-21 14:17:00 +00:00
|
|
|
|
|
|
|
// 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();
|
2017-06-12 11:13:15 +00:00
|
|
|
thread->InitFrame(&(instance->module()->functions[function_index]), args);
|
2017-04-06 10:57:24 +00:00
|
|
|
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
|
2017-06-12 11:13:15 +00:00
|
|
|
|
2017-11-21 14:17:00 +00:00
|
|
|
bool stack_overflow = isolate->has_pending_exception();
|
|
|
|
isolate->clear_pending_exception();
|
|
|
|
|
2016-10-20 14:27:23 +00:00
|
|
|
*possible_nondeterminism = thread->PossibleNondeterminism();
|
2017-12-02 00:30:37 +00:00
|
|
|
if (stack_overflow) return 0xDEADBEEF;
|
2017-11-21 14:17:00 +00:00
|
|
|
|
2017-12-02 00:30:37 +00:00
|
|
|
if (thread->state() == WasmInterpreter::TRAPPED) return 0xDEADBEEF;
|
2017-11-21 14:17:00 +00:00
|
|
|
|
|
|
|
if (interpreter_result == WasmInterpreter::FINISHED)
|
|
|
|
return thread->GetReturnValue().to<int32_t>();
|
|
|
|
|
|
|
|
thrower->RangeError(
|
|
|
|
"Interpreter did not finish execution within its step bound");
|
|
|
|
return -1;
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
|
2017-10-11 12:52:26 +00:00
|
|
|
MaybeHandle<WasmExportedFunction> GetExportedFunction(
|
|
|
|
Isolate* isolate, Handle<WasmInstanceObject> instance, const char* name) {
|
2016-09-12 12:26:37 +00:00
|
|
|
Handle<JSObject> exports_object;
|
2017-06-21 09:24:03 +00:00
|
|
|
Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
|
|
|
|
exports_object = Handle<JSObject>::cast(
|
|
|
|
JSObject::GetProperty(instance, exports).ToHandleChecked());
|
|
|
|
|
2016-09-12 12:26:37 +00:00
|
|
|
Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name);
|
|
|
|
PropertyDescriptor desc;
|
|
|
|
Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
|
|
|
|
isolate, exports_object, main_name, &desc);
|
2017-09-07 09:48:34 +00:00
|
|
|
if (!property_found.FromMaybe(false)) return {};
|
2017-09-11 11:03:05 +00:00
|
|
|
if (!desc.value()->IsJSFunction()) return {};
|
2016-09-12 12:26:37 +00:00
|
|
|
|
2017-09-07 09:48:34 +00:00
|
|
|
return Handle<WasmExportedFunction>::cast(desc.value());
|
|
|
|
}
|
|
|
|
|
2017-10-11 12:52:26 +00:00
|
|
|
int32_t CallWasmFunctionForTesting(Isolate* isolate,
|
|
|
|
Handle<WasmInstanceObject> instance,
|
2017-09-07 09:48:34 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-09-12 12:26:37 +00:00
|
|
|
|
|
|
|
// 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()) {
|
[wasm] Use pending exceptions consistently
In our internal code, we should only use pending exceptions. They will
be converted to scheduled exceptions on the API boundary.
Hence, the ErrorThrower just sets a pending exception; it should never
have to think about scheduled exceptions. The new
ScheduledErrorThrower inherits from ErrorThrower and reschedules any
pending exceptions in its destructor (turning them into scheduled
exceptions).
In some situations, there might already be a scheduled exception, e.g.
when calling other API methods (v8::Value::Get). In this case, the
ErrorThrower should also not set another pending exception. For the
reasons mentioned above, this can only be handled in the
ScheduledErrorThrower, which is used the API methods.
This fixes one DCHECK failure and one TODO about scheduled exceptions
if no instance can be created, because the start function throws.
R=mtrofin@chromium.org, mstarzinger@chromium.org
BUG=v8:6232,chromium:736256
Change-Id: I4905be04c565df9495de18fb26adbb5c05d193d2
Reviewed-on: https://chromium-review.googlesource.com/548641
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Mircea Trofin <mtrofin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46314}
2017-06-29 08:16:29 +00:00
|
|
|
DCHECK(isolate->has_pending_exception());
|
|
|
|
isolate->clear_pending_exception();
|
2017-06-09 12:21:52 +00:00
|
|
|
thrower->RuntimeError("Calling exported wasm function failed.");
|
2016-09-12 12:26:37 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
Handle<Object> result = retval.ToHandleChecked();
|
|
|
|
if (result->IsSmi()) {
|
2017-07-10 12:58:27 +00:00
|
|
|
return Smi::ToInt(*result);
|
2016-09-12 12:26:37 +00:00
|
|
|
}
|
|
|
|
if (result->IsHeapNumber()) {
|
|
|
|
return static_cast<int32_t>(HeapNumber::cast(*result)->value());
|
|
|
|
}
|
2016-10-13 16:17:44 +00:00
|
|
|
thrower->RuntimeError(
|
2017-06-09 12:21:52 +00:00
|
|
|
"Calling exported wasm function 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-09-04 12:15:18 +00:00
|
|
|
WasmJs::Install(isolate, true);
|
2016-09-17 01:30:09 +00:00
|
|
|
}
|
2017-06-12 11:13:15 +00:00
|
|
|
|
2016-09-12 12:26:37 +00:00
|
|
|
} // namespace testing
|
|
|
|
} // namespace wasm
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|