Line data Source code
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/handles.h"
8 : #include "src/isolate.h"
9 : #include "src/objects-inl.h"
10 : #include "src/objects/heap-number-inl.h"
11 : #include "src/property-descriptor.h"
12 : #include "src/wasm/module-decoder.h"
13 : #include "src/wasm/wasm-engine.h"
14 : #include "src/wasm/wasm-interpreter.h"
15 : #include "src/wasm/wasm-js.h"
16 : #include "src/wasm/wasm-module.h"
17 : #include "src/wasm/wasm-objects.h"
18 : #include "src/wasm/wasm-result.h"
19 :
20 : namespace v8 {
21 : namespace internal {
22 : namespace wasm {
23 : namespace testing {
24 :
25 0 : uint32_t GetInitialMemSize(const WasmModule* module) {
26 0 : return kWasmPageSize * module->initial_pages;
27 : }
28 :
29 228 : MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting(
30 : Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes) {
31 228 : auto enabled_features = WasmFeaturesFromIsolate(isolate);
32 : MaybeHandle<WasmModuleObject> module = isolate->wasm_engine()->SyncCompile(
33 228 : isolate, enabled_features, thrower, bytes);
34 : DCHECK_EQ(thrower->error(), module.is_null());
35 228 : if (module.is_null()) return {};
36 :
37 : return isolate->wasm_engine()->SyncInstantiate(
38 456 : isolate, thrower, module.ToHandleChecked(), {}, {});
39 : }
40 :
41 0 : std::shared_ptr<WasmModule> DecodeWasmModuleForTesting(
42 : Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
43 : const byte* module_end, ModuleOrigin origin, bool verify_functions) {
44 : // Decode the module, but don't verify function bodies, since we'll
45 : // be compiling them anyway.
46 0 : auto enabled_features = WasmFeaturesFromIsolate(isolate);
47 0 : ModuleResult decoding_result = DecodeWasmModule(
48 : enabled_features, module_start, module_end, verify_functions, origin,
49 0 : isolate->counters(), isolate->wasm_engine()->allocator());
50 :
51 0 : if (decoding_result.failed()) {
52 : // Module verification failed. throw.
53 : thrower->CompileError("DecodeWasmModule failed: %s",
54 0 : decoding_result.error().message().c_str());
55 : }
56 :
57 0 : return std::move(decoding_result).value();
58 : }
59 :
60 1 : bool InterpretWasmModuleForTesting(Isolate* isolate,
61 : Handle<WasmInstanceObject> instance,
62 : const char* name, size_t argc,
63 : WasmValue* args) {
64 : MaybeHandle<WasmExportedFunction> maybe_function =
65 1 : GetExportedFunction(isolate, instance, "main");
66 : Handle<WasmExportedFunction> function;
67 1 : if (!maybe_function.ToHandle(&function)) {
68 : return false;
69 : }
70 1 : int function_index = function->function_index();
71 3 : FunctionSig* signature = instance->module()->functions[function_index].sig;
72 1 : size_t param_count = signature->parameter_count();
73 1 : std::unique_ptr<WasmValue[]> arguments(new WasmValue[param_count]);
74 :
75 1 : size_t arg_count = std::min(param_count, argc);
76 1 : if (arg_count > 0) {
77 : memcpy(arguments.get(), args, arg_count);
78 : }
79 :
80 : // Fill the parameters up with default values.
81 1 : for (size_t i = argc; i < param_count; ++i) {
82 0 : switch (signature->GetParam(i)) {
83 : case kWasmI32:
84 0 : arguments[i] = WasmValue(int32_t{0});
85 0 : break;
86 : case kWasmI64:
87 0 : arguments[i] = WasmValue(int64_t{0});
88 0 : break;
89 : case kWasmF32:
90 0 : arguments[i] = WasmValue(0.0f);
91 0 : break;
92 : case kWasmF64:
93 0 : arguments[i] = WasmValue(0.0);
94 0 : break;
95 : default:
96 0 : UNREACHABLE();
97 : }
98 : }
99 :
100 : // Don't execute more than 16k steps.
101 : constexpr int kMaxNumSteps = 16 * 1024;
102 :
103 2 : Zone zone(isolate->allocator(), ZONE_NAME);
104 :
105 1 : WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
106 1 : WasmInterpreter::Thread* thread = interpreter->GetThread(0);
107 1 : thread->Reset();
108 :
109 : // Start an activation so that we can deal with stack overflows. We do not
110 : // finish the activation. An activation is just part of the state of the
111 : // interpreter, and we do not reuse the interpreter anyways. In addition,
112 : // finishing the activation is not correct in all cases, e.g. when the
113 : // execution of the interpreter did not finish after kMaxNumSteps.
114 1 : thread->StartActivation();
115 2 : thread->InitFrame(&instance->module()->functions[function_index],
116 1 : arguments.get());
117 1 : WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
118 :
119 : isolate->clear_pending_exception();
120 :
121 1 : return interpreter_result != WasmInterpreter::PAUSED;
122 : }
123 :
124 2275 : int32_t RunWasmModuleForTesting(Isolate* isolate,
125 : Handle<WasmInstanceObject> instance, int argc,
126 : Handle<Object> argv[]) {
127 2276 : ErrorThrower thrower(isolate, "RunWasmModule");
128 : return CallWasmFunctionForTesting(isolate, instance, &thrower, "main", argc,
129 4551 : argv);
130 : }
131 :
132 168 : int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
133 : const byte* module_end) {
134 : HandleScope scope(isolate);
135 168 : ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
136 : MaybeHandle<WasmInstanceObject> instance = CompileAndInstantiateForTesting(
137 168 : isolate, &thrower, ModuleWireBytes(module_start, module_end));
138 168 : if (instance.is_null()) {
139 : return -1;
140 : }
141 : return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
142 168 : nullptr);
143 : }
144 :
145 0 : int32_t CompileAndRunAsmWasmModule(Isolate* isolate, const byte* module_start,
146 : const byte* module_end) {
147 : HandleScope scope(isolate);
148 0 : ErrorThrower thrower(isolate, "CompileAndRunAsmWasmModule");
149 : MaybeHandle<AsmWasmData> data =
150 : isolate->wasm_engine()->SyncCompileTranslatedAsmJs(
151 : isolate, &thrower, ModuleWireBytes(module_start, module_end),
152 0 : Vector<const byte>(), Handle<HeapNumber>());
153 : DCHECK_EQ(thrower.error(), data.is_null());
154 0 : if (data.is_null()) return -1;
155 :
156 : MaybeHandle<WasmModuleObject> module =
157 : isolate->wasm_engine()->FinalizeTranslatedAsmJs(
158 0 : isolate, data.ToHandleChecked(), Handle<Script>::null());
159 :
160 : MaybeHandle<WasmInstanceObject> instance =
161 : isolate->wasm_engine()->SyncInstantiate(
162 : isolate, &thrower, module.ToHandleChecked(),
163 0 : Handle<JSReceiver>::null(), Handle<JSArrayBuffer>::null());
164 : DCHECK_EQ(thrower.error(), instance.is_null());
165 0 : if (instance.is_null()) return -1;
166 :
167 : return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
168 0 : nullptr);
169 : }
170 1 : WasmInterpretationResult InterpretWasmModule(
171 : Isolate* isolate, Handle<WasmInstanceObject> instance,
172 : int32_t function_index, WasmValue* args) {
173 : // Don't execute more than 16k steps.
174 : constexpr int kMaxNumSteps = 16 * 1024;
175 :
176 2 : Zone zone(isolate->allocator(), ZONE_NAME);
177 : v8::internal::HandleScope scope(isolate);
178 :
179 1 : WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
180 1 : WasmInterpreter::Thread* thread = interpreter->GetThread(0);
181 1 : thread->Reset();
182 :
183 : // Start an activation so that we can deal with stack overflows. We do not
184 : // finish the activation. An activation is just part of the state of the
185 : // interpreter, and we do not reuse the interpreter anyways. In addition,
186 : // finishing the activation is not correct in all cases, e.g. when the
187 : // execution of the interpreter did not finish after kMaxNumSteps.
188 1 : thread->StartActivation();
189 3 : thread->InitFrame(&(instance->module()->functions[function_index]), args);
190 1 : WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
191 :
192 : bool stack_overflow = isolate->has_pending_exception();
193 : isolate->clear_pending_exception();
194 :
195 1 : if (stack_overflow) return WasmInterpretationResult::Stopped();
196 :
197 1 : if (thread->state() == WasmInterpreter::TRAPPED) {
198 0 : return WasmInterpretationResult::Trapped(thread->PossibleNondeterminism());
199 : }
200 :
201 1 : if (interpreter_result == WasmInterpreter::FINISHED) {
202 : return WasmInterpretationResult::Finished(
203 2 : thread->GetReturnValue().to<int32_t>(),
204 1 : thread->PossibleNondeterminism());
205 : }
206 :
207 : return WasmInterpretationResult::Stopped();
208 : }
209 :
210 2293 : MaybeHandle<WasmExportedFunction> GetExportedFunction(
211 : Isolate* isolate, Handle<WasmInstanceObject> instance, const char* name) {
212 : Handle<JSObject> exports_object;
213 2293 : Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
214 : exports_object = Handle<JSObject>::cast(
215 4588 : JSObject::GetProperty(isolate, instance, exports).ToHandleChecked());
216 :
217 2294 : Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name);
218 : PropertyDescriptor desc;
219 : Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
220 2294 : isolate, exports_object, main_name, &desc);
221 2294 : if (!property_found.FromMaybe(false)) return {};
222 2294 : if (!desc.value()->IsJSFunction()) return {};
223 :
224 2294 : return Handle<WasmExportedFunction>::cast(desc.value());
225 : }
226 :
227 2292 : int32_t CallWasmFunctionForTesting(Isolate* isolate,
228 : Handle<WasmInstanceObject> instance,
229 : ErrorThrower* thrower, const char* name,
230 : int argc, Handle<Object> argv[]) {
231 : MaybeHandle<WasmExportedFunction> maybe_export =
232 2292 : GetExportedFunction(isolate, instance, name);
233 : Handle<WasmExportedFunction> main_export;
234 2292 : if (!maybe_export.ToHandle(&main_export)) {
235 : return -1;
236 : }
237 :
238 : // Call the JS function.
239 : Handle<Object> undefined = isolate->factory()->undefined_value();
240 : MaybeHandle<Object> retval =
241 2292 : Execution::Call(isolate, main_export, undefined, argc, argv);
242 :
243 : // The result should be a number.
244 2293 : if (retval.is_null()) {
245 : DCHECK(isolate->has_pending_exception());
246 : isolate->clear_pending_exception();
247 40 : thrower->RuntimeError("Calling exported wasm function failed.");
248 40 : return -1;
249 : }
250 : Handle<Object> result = retval.ToHandleChecked();
251 2253 : if (result->IsSmi()) {
252 2173 : return Smi::ToInt(*result);
253 : }
254 80 : if (result->IsHeapNumber()) {
255 80 : return static_cast<int32_t>(HeapNumber::cast(*result)->value());
256 : }
257 : thrower->RuntimeError(
258 0 : "Calling exported wasm function failed: Return value should be number");
259 0 : return -1;
260 : }
261 :
262 354 : void SetupIsolateForWasmModule(Isolate* isolate) {
263 354 : WasmJs::Install(isolate, true);
264 354 : }
265 :
266 : } // namespace testing
267 : } // namespace wasm
268 : } // namespace internal
269 53290 : } // namespace v8
|