// 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-opcodes.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(), {}, {}); } OwnedVector MakeDefaultInterpreterArguments(Isolate* isolate, const FunctionSig* sig) { size_t param_count = sig->parameter_count(); auto arguments = OwnedVector::New(param_count); for (size_t i = 0; i < param_count; ++i) { switch (sig->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::kS128: arguments[i] = WasmValue(Simd128{}); 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: UNREACHABLE(); } } return arguments; } OwnedVector> MakeDefaultArguments(Isolate* isolate, const FunctionSig* sig) { size_t param_count = sig->parameter_count(); auto arguments = OwnedVector>::New(param_count); for (size_t i = 0; i < param_count; ++i) { switch (sig->GetParam(i).kind()) { case ValueType::kI32: case ValueType::kF32: case ValueType::kF64: case ValueType::kS128: // Argument here for kS128 does not matter as we should error out before // hitting this case. arguments[i] = handle(Smi::zero(), isolate); break; case ValueType::kI64: arguments[i] = BigInt::FromInt64(isolate, 0); break; case ValueType::kOptRef: arguments[i] = isolate->factory()->null_value(); break; case ValueType::kRef: case ValueType::kRtt: case ValueType::kI8: case ValueType::kI16: case ValueType::kStmt: case ValueType::kBottom: UNREACHABLE(); } } return arguments; } 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); const WasmFunction* func = &instance->module()->functions[function_index]; CHECK(func->exported); // This would normally be handled by export wrappers. if (!IsJSCompatibleSignature(func->sig, instance->module(), WasmFeatures::FromIsolate(isolate))) { return WasmInterpretationResult::Trapped(false); } WasmInterpreter interpreter{ isolate, instance->module(), ModuleWireBytes{instance->module_object().native_module()->wire_bytes()}, instance}; interpreter.InitFrame(func, args); WasmInterpreter::State interpreter_result = interpreter.Run(kMaxNumSteps); 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) { // Get the result as an {int32_t}. Keep this in sync with // {CallWasmFunctionForTesting}, because fuzzers will compare the results. int32_t result = -1; if (func->sig->return_count() > 0) { WasmValue return_value = interpreter.GetReturnValue(); switch (func->sig->GetReturn(0).kind()) { case ValueType::kI32: result = return_value.to(); break; case ValueType::kI64: result = static_cast(return_value.to()); break; case ValueType::kF32: result = static_cast(return_value.to()); break; case ValueType::kF64: result = static_cast(return_value.to()); break; default: break; } } return WasmInterpretationResult::Finished( result, 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(); } 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[], bool* exception) { if (exception) *exception = false; 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(); if (exception) *exception = true; return -1; } Handle result = retval.ToHandleChecked(); // Multi-value returns, get the first return value (see InterpretWasmModule). if (result->IsJSArray()) { auto receiver = Handle::cast(result); result = JSObject::GetElement(isolate, receiver, 0).ToHandleChecked(); } if (result->IsSmi()) { return Smi::ToInt(*result); } if (result->IsHeapNumber()) { return static_cast(HeapNumber::cast(*result).value()); } if (result->IsBigInt()) { return static_cast(BigInt::cast(*result).AsInt64()); } return -1; } void SetupIsolateForWasmModule(Isolate* isolate) { WasmJs::Install(isolate, true); } } // namespace testing } // namespace wasm } // namespace internal } // namespace v8