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