1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "test/common/wasm/wasm-module-runner.h"
6
7 #include "src/execution/isolate.h"
8 #include "src/handles/handles.h"
9 #include "src/objects/heap-number-inl.h"
10 #include "src/objects/objects-inl.h"
11 #include "src/objects/property-descriptor.h"
12 #include "src/wasm/module-decoder.h"
13 #include "src/wasm/wasm-engine.h"
14 #include "src/wasm/wasm-js.h"
15 #include "src/wasm/wasm-module.h"
16 #include "src/wasm/wasm-objects.h"
17 #include "src/wasm/wasm-opcodes.h"
18 #include "src/wasm/wasm-result.h"
19 #include "test/common/wasm/wasm-interpreter.h"
20
21 namespace v8 {
22 namespace internal {
23 namespace wasm {
24 namespace testing {
25
CompileForTesting(Isolate * isolate,ErrorThrower * thrower,const ModuleWireBytes & bytes)26 MaybeHandle<WasmModuleObject> CompileForTesting(Isolate* isolate,
27 ErrorThrower* thrower,
28 const ModuleWireBytes& bytes) {
29 auto enabled_features = WasmFeatures::FromIsolate(isolate);
30 MaybeHandle<WasmModuleObject> module =
31 GetWasmEngine()->SyncCompile(isolate, enabled_features, thrower, bytes);
32 DCHECK_EQ(thrower->error(), module.is_null());
33 return module;
34 }
35
CompileAndInstantiateForTesting(Isolate * isolate,ErrorThrower * thrower,const ModuleWireBytes & bytes)36 MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting(
37 Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes) {
38 MaybeHandle<WasmModuleObject> module =
39 CompileForTesting(isolate, thrower, bytes);
40 if (module.is_null()) return {};
41 return GetWasmEngine()->SyncInstantiate(isolate, thrower,
42 module.ToHandleChecked(), {}, {});
43 }
44
MakeDefaultInterpreterArguments(Isolate * isolate,const FunctionSig * sig)45 base::OwnedVector<WasmValue> MakeDefaultInterpreterArguments(
46 Isolate* isolate, const FunctionSig* sig) {
47 size_t param_count = sig->parameter_count();
48 auto arguments = base::OwnedVector<WasmValue>::New(param_count);
49
50 for (size_t i = 0; i < param_count; ++i) {
51 switch (sig->GetParam(i).kind()) {
52 case kI32:
53 arguments[i] = WasmValue(static_cast<int32_t>(i));
54 break;
55 case kI64:
56 arguments[i] = WasmValue(static_cast<int64_t>(i));
57 break;
58 case kF32:
59 arguments[i] = WasmValue(static_cast<float>(i));
60 break;
61 case kF64:
62 arguments[i] = WasmValue(static_cast<double>(i));
63 break;
64 case kS128: {
65 uint8_t s128_bytes[sizeof(Simd128)] = {static_cast<uint8_t>(i)};
66 arguments[i] = WasmValue(Simd128{s128_bytes});
67 break;
68 }
69 case kOptRef:
70 arguments[i] =
71 WasmValue(Handle<Object>::cast(isolate->factory()->null_value()),
72 sig->GetParam(i));
73 break;
74 case kRef:
75 case kRtt:
76 case kRttWithDepth:
77 case kI8:
78 case kI16:
79 case kVoid:
80 case kBottom:
81 UNREACHABLE();
82 }
83 }
84
85 return arguments;
86 }
87
MakeDefaultArguments(Isolate * isolate,const FunctionSig * sig)88 base::OwnedVector<Handle<Object>> MakeDefaultArguments(Isolate* isolate,
89 const FunctionSig* sig) {
90 size_t param_count = sig->parameter_count();
91 auto arguments = base::OwnedVector<Handle<Object>>::New(param_count);
92
93 for (size_t i = 0; i < param_count; ++i) {
94 switch (sig->GetParam(i).kind()) {
95 case kI32:
96 case kF32:
97 case kF64:
98 case kS128:
99 // Argument here for kS128 does not matter as we should error out before
100 // hitting this case.
101 arguments[i] = handle(Smi::FromInt(static_cast<int>(i)), isolate);
102 break;
103 case kI64:
104 arguments[i] = BigInt::FromInt64(isolate, static_cast<int64_t>(i));
105 break;
106 case kOptRef:
107 arguments[i] = isolate->factory()->null_value();
108 break;
109 case kRef:
110 case kRtt:
111 case kRttWithDepth:
112 case kI8:
113 case kI16:
114 case kVoid:
115 case kBottom:
116 UNREACHABLE();
117 }
118 }
119
120 return arguments;
121 }
122
CompileAndRunWasmModule(Isolate * isolate,const byte * module_start,const byte * module_end)123 int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
124 const byte* module_end) {
125 HandleScope scope(isolate);
126 ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
127 MaybeHandle<WasmInstanceObject> instance = CompileAndInstantiateForTesting(
128 isolate, &thrower, ModuleWireBytes(module_start, module_end));
129 if (instance.is_null()) {
130 return -1;
131 }
132 return CallWasmFunctionForTesting(isolate, instance.ToHandleChecked(), "main",
133 0, nullptr);
134 }
135
InterpretWasmModule(Isolate * isolate,Handle<WasmInstanceObject> instance,int32_t function_index,WasmValue * args)136 WasmInterpretationResult InterpretWasmModule(
137 Isolate* isolate, Handle<WasmInstanceObject> instance,
138 int32_t function_index, WasmValue* args) {
139 // Don't execute more than 16k steps.
140 constexpr int kMaxNumSteps = 16 * 1024;
141
142 Zone zone(isolate->allocator(), ZONE_NAME);
143 v8::internal::HandleScope scope(isolate);
144 const WasmFunction* func = &instance->module()->functions[function_index];
145
146 CHECK(func->exported);
147 // This would normally be handled by export wrappers.
148 if (!IsJSCompatibleSignature(func->sig, instance->module(),
149 WasmFeatures::FromIsolate(isolate))) {
150 return WasmInterpretationResult::Trapped(false);
151 }
152
153 WasmInterpreter interpreter{
154 isolate, instance->module(),
155 ModuleWireBytes{instance->module_object().native_module()->wire_bytes()},
156 instance};
157 interpreter.InitFrame(func, args);
158 WasmInterpreter::State interpreter_result = interpreter.Run(kMaxNumSteps);
159
160 bool stack_overflow = isolate->has_pending_exception();
161 isolate->clear_pending_exception();
162
163 if (stack_overflow) return WasmInterpretationResult::Failed();
164
165 if (interpreter.state() == WasmInterpreter::TRAPPED) {
166 return WasmInterpretationResult::Trapped(
167 interpreter.PossibleNondeterminism());
168 }
169
170 if (interpreter_result == WasmInterpreter::FINISHED) {
171 // Get the result as an {int32_t}. Keep this in sync with
172 // {CallWasmFunctionForTesting}, because fuzzers will compare the results.
173 int32_t result = -1;
174 if (func->sig->return_count() > 0) {
175 WasmValue return_value = interpreter.GetReturnValue();
176 switch (func->sig->GetReturn(0).kind()) {
177 case kI32:
178 result = return_value.to<int32_t>();
179 break;
180 case kI64:
181 result = static_cast<int32_t>(return_value.to<int64_t>());
182 break;
183 case kF32:
184 result = static_cast<int32_t>(return_value.to<float>());
185 break;
186 case kF64:
187 result = static_cast<int32_t>(return_value.to<double>());
188 break;
189 default:
190 break;
191 }
192 }
193 return WasmInterpretationResult::Finished(
194 result, interpreter.PossibleNondeterminism());
195 }
196
197 // The interpreter did not finish within the limited number of steps, so it
198 // might execute an infinite loop or infinite recursion. Return "failed"
199 // status in that case.
200 return WasmInterpretationResult::Failed();
201 }
202
GetExportedFunction(Isolate * isolate,Handle<WasmInstanceObject> instance,const char * name)203 MaybeHandle<WasmExportedFunction> GetExportedFunction(
204 Isolate* isolate, Handle<WasmInstanceObject> instance, const char* name) {
205 Handle<JSObject> exports_object;
206 Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
207 exports_object = Handle<JSObject>::cast(
208 JSObject::GetProperty(isolate, instance, exports).ToHandleChecked());
209
210 Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name);
211 PropertyDescriptor desc;
212 Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
213 isolate, exports_object, main_name, &desc);
214 if (!property_found.FromMaybe(false)) return {};
215 if (!desc.value()->IsJSFunction()) return {};
216
217 return Handle<WasmExportedFunction>::cast(desc.value());
218 }
219
CallWasmFunctionForTesting(Isolate * isolate,Handle<WasmInstanceObject> instance,const char * name,int argc,Handle<Object> argv[],bool * exception)220 int32_t CallWasmFunctionForTesting(Isolate* isolate,
221 Handle<WasmInstanceObject> instance,
222 const char* name, int argc,
223 Handle<Object> argv[], bool* exception) {
224 if (exception) *exception = false;
225 MaybeHandle<WasmExportedFunction> maybe_export =
226 GetExportedFunction(isolate, instance, name);
227 Handle<WasmExportedFunction> main_export;
228 if (!maybe_export.ToHandle(&main_export)) {
229 return -1;
230 }
231
232 // Call the JS function.
233 Handle<Object> undefined = isolate->factory()->undefined_value();
234 MaybeHandle<Object> retval =
235 Execution::Call(isolate, main_export, undefined, argc, argv);
236
237 // The result should be a number.
238 if (retval.is_null()) {
239 DCHECK(isolate->has_pending_exception());
240 isolate->clear_pending_exception();
241 if (exception) *exception = true;
242 return -1;
243 }
244 Handle<Object> result = retval.ToHandleChecked();
245
246 // Multi-value returns, get the first return value (see InterpretWasmModule).
247 if (result->IsJSArray()) {
248 auto receiver = Handle<JSReceiver>::cast(result);
249 result = JSObject::GetElement(isolate, receiver, 0).ToHandleChecked();
250 }
251
252 if (result->IsSmi()) {
253 return Smi::ToInt(*result);
254 }
255 if (result->IsHeapNumber()) {
256 return static_cast<int32_t>(HeapNumber::cast(*result).value());
257 }
258 if (result->IsBigInt()) {
259 return static_cast<int32_t>(BigInt::cast(*result).AsInt64());
260 }
261 return -1;
262 }
263
SetupIsolateForWasmModule(Isolate * isolate)264 void SetupIsolateForWasmModule(Isolate* isolate) {
265 WasmJs::Install(isolate, true);
266 }
267
268 } // namespace testing
269 } // namespace wasm
270 } // namespace internal
271 } // namespace v8
272