// 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 CompileForTesting(Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes) { auto enabled_features = WasmFeatures::FromIsolate(isolate); MaybeHandle module = isolate->wasm_engine()->SyncCompile( isolate, enabled_features, thrower, bytes); DCHECK_EQ(thrower->error(), module.is_null()); return module; } MaybeHandle CompileAndInstantiateForTesting( Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes) { MaybeHandle 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(), 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 instance, size_t argc, WasmValue* args) { HandleScope handle_scope(isolate); // Avoid leaking handles. WasmCodeRefScope code_ref_scope; Handle 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 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::cast(isolate->factory()->null_value())); break; 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 CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, const byte* module_end) { HandleScope scope(isolate); ErrorThrower thrower(isolate, "CompileAndRunWasmModule"); MaybeHandle instance = CompileAndInstantiateForTesting( isolate, &thrower, ModuleWireBytes(module_start, module_end)); if (instance.is_null()) { return -1; } return CallWasmFunctionForTesting(isolate, instance.ToHandleChecked(), "main", 0, nullptr); } WasmInterpretationResult InterpretWasmModule( Isolate* isolate, Handle 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 GetExportedFunction( Isolate* isolate, Handle instance, const char* name) { Handle exports_object; Handle exports = isolate->factory()->InternalizeUtf8String("exports"); exports_object = Handle::cast( JSObject::GetProperty(isolate, instance, exports).ToHandleChecked()); Handle main_name = isolate->factory()->NewStringFromAsciiChecked(name); PropertyDescriptor desc; Maybe property_found = JSReceiver::GetOwnPropertyDescriptor( isolate, exports_object, main_name, &desc); if (!property_found.FromMaybe(false)) return {}; if (!desc.value()->IsJSFunction()) return {}; return Handle::cast(desc.value()); } int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle instance, const char* name, int argc, Handle argv[]) { MaybeHandle maybe_export = GetExportedFunction(isolate, instance, name); Handle main_export; if (!maybe_export.ToHandle(&main_export)) { return -1; } // Call the JS function. Handle undefined = isolate->factory()->undefined_value(); MaybeHandle 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(); return -1; } Handle result = retval.ToHandleChecked(); if (result->IsSmi()) { return Smi::ToInt(*result); } if (result->IsHeapNumber()) { return static_cast(HeapNumber::cast(*result).value()); } return -1; } void SetupIsolateForWasmModule(Isolate* isolate) { WasmJs::Install(isolate, true); } } // namespace testing } // namespace wasm } // namespace internal } // namespace v8