OLD | NEW |
| (Empty) |
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/cctest/wasm/wasm-module-runner.h" | |
6 | |
7 #include "src/handles.h" | |
8 #include "src/isolate.h" | |
9 #include "src/objects.h" | |
10 #include "src/property-descriptor.h" | |
11 #include "src/wasm/module-decoder.h" | |
12 #include "src/wasm/wasm-interpreter.h" | |
13 #include "src/wasm/wasm-module.h" | |
14 #include "src/wasm/wasm-result.h" | |
15 #include "src/zone.h" | |
16 | |
17 namespace v8 { | |
18 namespace internal { | |
19 namespace wasm { | |
20 namespace testing { | |
21 | |
22 uint32_t GetMinModuleMemSize(const WasmModule* module) { | |
23 return WasmModule::kPageSize * module->min_mem_pages; | |
24 } | |
25 | |
26 const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone, | |
27 ErrorThrower& thrower, | |
28 const byte* module_start, | |
29 const byte* module_end, | |
30 ModuleOrigin origin) { | |
31 // Decode the module, but don't verify function bodies, since we'll | |
32 // be compiling them anyway. | |
33 ModuleResult decoding_result = | |
34 DecodeWasmModule(isolate, zone, module_start, module_end, false, origin); | |
35 | |
36 std::unique_ptr<const WasmModule> module(decoding_result.val); | |
37 if (decoding_result.failed()) { | |
38 // Module verification failed. throw. | |
39 thrower.Error("WASM.compileRun() failed: %s", | |
40 decoding_result.error_msg.get()); | |
41 return nullptr; | |
42 } | |
43 | |
44 if (thrower.error()) return nullptr; | |
45 return module.release(); | |
46 } | |
47 | |
48 const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate, | |
49 ErrorThrower& thrower, | |
50 const WasmModule* module) { | |
51 CHECK(module != nullptr); | |
52 | |
53 if (module->import_table.size() > 0) { | |
54 thrower.Error("Not supported: module has imports."); | |
55 } | |
56 if (module->export_table.size() == 0) { | |
57 thrower.Error("Not supported: module has no exports."); | |
58 } | |
59 | |
60 if (thrower.error()) return Handle<JSObject>::null(); | |
61 | |
62 // Although we decoded the module for some pre-validation, run the bytes | |
63 // again through the normal pipeline. | |
64 MaybeHandle<JSObject> module_object = CreateModuleObjectFromBytes( | |
65 isolate, module->module_start, module->module_end, &thrower, | |
66 ModuleOrigin::kWasmOrigin); | |
67 if (module_object.is_null()) return Handle<JSObject>::null(); | |
68 return WasmModule::Instantiate(isolate, module_object.ToHandleChecked(), | |
69 Handle<JSReceiver>::null(), | |
70 Handle<JSArrayBuffer>::null()) | |
71 .ToHandleChecked(); | |
72 } | |
73 | |
74 int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, | |
75 const byte* module_end, ModuleOrigin origin) { | |
76 HandleScope scope(isolate); | |
77 Zone zone(isolate->allocator()); | |
78 | |
79 ErrorThrower thrower(isolate, "CompileAndRunWasmModule"); | |
80 std::unique_ptr<const WasmModule> module(DecodeWasmModuleForTesting( | |
81 isolate, &zone, thrower, module_start, module_end, origin)); | |
82 | |
83 if (module == nullptr) { | |
84 return -1; | |
85 } | |
86 Handle<JSObject> instance = | |
87 InstantiateModuleForTesting(isolate, thrower, module.get()); | |
88 if (instance.is_null()) { | |
89 return -1; | |
90 } | |
91 const char* f_name = origin == ModuleOrigin::kAsmJsOrigin ? "caller" : "main"; | |
92 return CallWasmFunctionForTesting(isolate, instance, thrower, f_name, 0, | |
93 nullptr, origin); | |
94 } | |
95 | |
96 int32_t InterpretWasmModule(Isolate* isolate, ErrorThrower& thrower, | |
97 const WasmModule* module, int function_index, | |
98 WasmVal* args) { | |
99 CHECK(module != nullptr); | |
100 | |
101 Zone zone(isolate->allocator()); | |
102 v8::internal::HandleScope scope(isolate); | |
103 | |
104 if (module->import_table.size() > 0) { | |
105 thrower.Error("Not supported: module has imports."); | |
106 } | |
107 if (module->export_table.size() == 0) { | |
108 thrower.Error("Not supported: module has no exports."); | |
109 } | |
110 | |
111 if (thrower.error()) return -1; | |
112 | |
113 WasmModuleInstance instance(module); | |
114 instance.context = isolate->native_context(); | |
115 instance.mem_size = GetMinModuleMemSize(module); | |
116 instance.mem_start = nullptr; | |
117 instance.globals_start = nullptr; | |
118 | |
119 ModuleEnv module_env; | |
120 module_env.module = module; | |
121 module_env.instance = &instance; | |
122 module_env.origin = module->origin; | |
123 | |
124 const WasmFunction* function = &(module->functions[function_index]); | |
125 | |
126 FunctionBody body = {&module_env, function->sig, module->module_start, | |
127 module->module_start + function->code_start_offset, | |
128 module->module_start + function->code_end_offset}; | |
129 DecodeResult result = VerifyWasmCode(isolate->allocator(), body); | |
130 if (result.failed()) { | |
131 thrower.Error("Function did not verify"); | |
132 return -1; | |
133 } | |
134 | |
135 WasmInterpreter interpreter(&instance, isolate->allocator()); | |
136 | |
137 WasmInterpreter::Thread* thread = interpreter.GetThread(0); | |
138 thread->Reset(); | |
139 thread->PushFrame(function, args); | |
140 if (thread->Run() == WasmInterpreter::FINISHED) { | |
141 WasmVal val = thread->GetReturnValue(); | |
142 return val.to<int32_t>(); | |
143 } else if (thread->state() == WasmInterpreter::TRAPPED) { | |
144 return 0xdeadbeef; | |
145 } else { | |
146 thrower.Error("Interpreter did not finish execution within its step bound"); | |
147 return -1; | |
148 } | |
149 } | |
150 | |
151 int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle<JSObject> instance, | |
152 ErrorThrower& thrower, const char* name, | |
153 int argc, Handle<Object> argv[], | |
154 ModuleOrigin origin) { | |
155 Handle<JSObject> exports_object; | |
156 if (origin == ModuleOrigin::kAsmJsOrigin) { | |
157 exports_object = instance; | |
158 } else { | |
159 Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports"); | |
160 exports_object = Handle<JSObject>::cast( | |
161 JSObject::GetProperty(instance, exports).ToHandleChecked()); | |
162 } | |
163 Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name); | |
164 PropertyDescriptor desc; | |
165 Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor( | |
166 isolate, exports_object, main_name, &desc); | |
167 if (!property_found.FromMaybe(false)) return -1; | |
168 | |
169 Handle<JSFunction> main_export = Handle<JSFunction>::cast(desc.value()); | |
170 | |
171 // Call the JS function. | |
172 Handle<Object> undefined = isolate->factory()->undefined_value(); | |
173 MaybeHandle<Object> retval = | |
174 Execution::Call(isolate, main_export, undefined, argc, argv); | |
175 | |
176 // The result should be a number. | |
177 if (retval.is_null()) { | |
178 thrower.Error("WASM.compileRun() failed: Invocation was null"); | |
179 return -1; | |
180 } | |
181 Handle<Object> result = retval.ToHandleChecked(); | |
182 if (result->IsSmi()) { | |
183 return Smi::cast(*result)->value(); | |
184 } | |
185 if (result->IsHeapNumber()) { | |
186 return static_cast<int32_t>(HeapNumber::cast(*result)->value()); | |
187 } | |
188 thrower.Error("WASM.compileRun() failed: Return value should be number"); | |
189 return -1; | |
190 } | |
191 | |
192 } // namespace testing | |
193 } // namespace wasm | |
194 } // namespace internal | |
195 } // namespace v8 | |
OLD | NEW |