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