| 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 |