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