Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1 | // Copyright 2015 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 "src/api.h" |
| 6 | #include "src/api-natives.h" |
| 7 | #include "src/assert-scope.h" |
| 8 | #include "src/ast/ast.h" |
| 9 | #include "src/ast/scopes.h" |
| 10 | #include "src/factory.h" |
| 11 | #include "src/handles.h" |
| 12 | #include "src/isolate.h" |
| 13 | #include "src/objects.h" |
| 14 | #include "src/parsing/parser.h" |
| 15 | #include "src/typing-asm.h" |
| 16 | |
| 17 | #include "src/wasm/asm-wasm-builder.h" |
| 18 | #include "src/wasm/encoder.h" |
| 19 | #include "src/wasm/module-decoder.h" |
| 20 | #include "src/wasm/wasm-js.h" |
| 21 | #include "src/wasm/wasm-module.h" |
| 22 | #include "src/wasm/wasm-result.h" |
| 23 | |
| 24 | typedef uint8_t byte; |
| 25 | |
| 26 | using v8::internal::wasm::ErrorThrower; |
| 27 | |
| 28 | namespace v8 { |
| 29 | |
| 30 | namespace { |
| 31 | struct RawBuffer { |
| 32 | const byte* start; |
| 33 | const byte* end; |
| 34 | size_t size() { return static_cast<size_t>(end - start); } |
| 35 | }; |
| 36 | |
| 37 | |
| 38 | RawBuffer GetRawBufferArgument( |
| 39 | ErrorThrower& thrower, const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 40 | if (args.Length() < 1 || !args[0]->IsArrayBuffer()) { |
| 41 | thrower.Error("Argument 0 must be an array buffer"); |
| 42 | return {nullptr, nullptr}; |
| 43 | } |
| 44 | Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]); |
| 45 | ArrayBuffer::Contents contents = buffer->GetContents(); |
| 46 | |
| 47 | // TODO(titzer): allow offsets into buffers, views, etc. |
| 48 | |
| 49 | const byte* start = reinterpret_cast<const byte*>(contents.Data()); |
| 50 | const byte* end = start + contents.ByteLength(); |
| 51 | |
| 52 | if (start == nullptr) { |
| 53 | thrower.Error("ArrayBuffer argument is empty"); |
| 54 | } |
| 55 | return {start, end}; |
| 56 | } |
| 57 | |
| 58 | |
| 59 | void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 60 | HandleScope scope(args.GetIsolate()); |
| 61 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
| 62 | ErrorThrower thrower(isolate, "WASM.verifyModule()"); |
| 63 | |
| 64 | RawBuffer buffer = GetRawBufferArgument(thrower, args); |
| 65 | if (thrower.error()) return; |
| 66 | |
| 67 | i::Zone zone; |
| 68 | internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( |
| 69 | isolate, &zone, buffer.start, buffer.end, true, false); |
| 70 | |
| 71 | if (result.failed()) { |
| 72 | thrower.Failed("", result); |
| 73 | } |
| 74 | |
| 75 | if (result.val) delete result.val; |
| 76 | } |
| 77 | |
| 78 | |
| 79 | void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 80 | HandleScope scope(args.GetIsolate()); |
| 81 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
| 82 | ErrorThrower thrower(isolate, "WASM.verifyFunction()"); |
| 83 | |
| 84 | RawBuffer buffer = GetRawBufferArgument(thrower, args); |
| 85 | if (thrower.error()) return; |
| 86 | |
| 87 | internal::wasm::FunctionResult result; |
| 88 | { |
| 89 | // Verification of a single function shouldn't allocate. |
| 90 | i::DisallowHeapAllocation no_allocation; |
| 91 | i::Zone zone; |
| 92 | result = internal::wasm::DecodeWasmFunction(isolate, &zone, nullptr, |
| 93 | buffer.start, buffer.end); |
| 94 | } |
| 95 | |
| 96 | if (result.failed()) { |
| 97 | thrower.Failed("", result); |
| 98 | } |
| 99 | |
| 100 | if (result.val) delete result.val; |
| 101 | } |
| 102 | |
| 103 | |
| 104 | void CompileRun(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 105 | HandleScope scope(args.GetIsolate()); |
| 106 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
| 107 | ErrorThrower thrower(isolate, "WASM.compileRun()"); |
| 108 | |
| 109 | RawBuffer buffer = GetRawBufferArgument(thrower, args); |
| 110 | if (thrower.error()) return; |
| 111 | |
| 112 | // Decode and pre-verify the functions before compiling and running. |
| 113 | i::Zone zone; |
| 114 | internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( |
| 115 | isolate, &zone, buffer.start, buffer.end, true, false); |
| 116 | |
| 117 | if (result.failed()) { |
| 118 | thrower.Failed("", result); |
| 119 | } else { |
| 120 | // Success. Compile and run! |
| 121 | int32_t retval = i::wasm::CompileAndRunWasmModule(isolate, result.val); |
| 122 | args.GetReturnValue().Set(retval); |
| 123 | } |
| 124 | |
| 125 | if (result.val) delete result.val; |
| 126 | } |
| 127 | |
| 128 | |
| 129 | v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(i::ParseInfo* info) { |
| 130 | info->set_global(); |
| 131 | info->set_lazy(false); |
| 132 | info->set_allow_lazy_parsing(false); |
| 133 | info->set_toplevel(true); |
| 134 | |
| 135 | if (!i::Compiler::ParseAndAnalyze(info)) { |
| 136 | return nullptr; |
| 137 | } |
| 138 | |
| 139 | info->set_literal( |
| 140 | info->scope()->declarations()->at(0)->AsFunctionDeclaration()->fun()); |
| 141 | |
| 142 | v8::internal::AsmTyper typer(info->isolate(), info->zone(), *(info->script()), |
| 143 | info->literal()); |
| 144 | if (!typer.Validate()) { |
| 145 | return nullptr; |
| 146 | } |
| 147 | |
| 148 | auto module = v8::internal::wasm::AsmWasmBuilder( |
| 149 | info->isolate(), info->zone(), info->literal()) |
| 150 | .Run(); |
| 151 | return module; |
| 152 | } |
| 153 | |
| 154 | |
| 155 | void AsmCompileRun(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 156 | HandleScope scope(args.GetIsolate()); |
| 157 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
| 158 | ErrorThrower thrower(isolate, "WASM.asmCompileRun()"); |
| 159 | |
| 160 | if (args.Length() != 1) { |
| 161 | thrower.Error("Invalid argument count"); |
| 162 | return; |
| 163 | } |
| 164 | if (!args[0]->IsString()) { |
| 165 | thrower.Error("Invalid argument count"); |
| 166 | return; |
| 167 | } |
| 168 | |
| 169 | i::Factory* factory = isolate->factory(); |
| 170 | i::Zone zone; |
| 171 | Local<String> source = Local<String>::Cast(args[0]); |
| 172 | i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source)); |
| 173 | i::ParseInfo info(&zone, script); |
| 174 | |
| 175 | auto module = TranslateAsmModule(&info); |
| 176 | if (module == nullptr) { |
| 177 | thrower.Error("Asm.js validation failed"); |
| 178 | return; |
| 179 | } |
| 180 | |
| 181 | int32_t result = v8::internal::wasm::CompileAndRunWasmModule( |
| 182 | isolate, module->Begin(), module->End(), true); |
| 183 | args.GetReturnValue().Set(result); |
| 184 | } |
| 185 | |
| 186 | |
| 187 | // TODO(aseemgarg): deal with arraybuffer and foreign functions |
| 188 | void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 189 | HandleScope scope(args.GetIsolate()); |
| 190 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
| 191 | ErrorThrower thrower(isolate, "WASM.instantiateModuleFromAsm()"); |
| 192 | |
| 193 | if (args.Length() != 1) { |
| 194 | thrower.Error("Invalid argument count"); |
| 195 | return; |
| 196 | } |
| 197 | if (!args[0]->IsString()) { |
| 198 | thrower.Error("Invalid argument count"); |
| 199 | return; |
| 200 | } |
| 201 | |
| 202 | i::Factory* factory = isolate->factory(); |
| 203 | i::Zone zone; |
| 204 | Local<String> source = Local<String>::Cast(args[0]); |
| 205 | i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source)); |
| 206 | i::ParseInfo info(&zone, script); |
| 207 | |
| 208 | auto module = TranslateAsmModule(&info); |
| 209 | if (module == nullptr) { |
| 210 | thrower.Error("Asm.js validation failed"); |
| 211 | return; |
| 212 | } |
| 213 | |
| 214 | i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null(); |
| 215 | internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( |
| 216 | isolate, &zone, module->Begin(), module->End(), false, false); |
| 217 | |
| 218 | if (result.failed()) { |
| 219 | thrower.Failed("", result); |
| 220 | } else { |
| 221 | // Success. Instantiate the module and return the object. |
| 222 | i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null(); |
| 223 | |
| 224 | i::MaybeHandle<i::JSObject> object = |
| 225 | result.val->Instantiate(isolate, ffi, memory); |
| 226 | |
| 227 | if (!object.is_null()) { |
| 228 | args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked())); |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | if (result.val) delete result.val; |
| 233 | } |
| 234 | |
| 235 | |
| 236 | void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 237 | HandleScope scope(args.GetIsolate()); |
| 238 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
| 239 | ErrorThrower thrower(isolate, "WASM.instantiateModule()"); |
| 240 | |
| 241 | RawBuffer buffer = GetRawBufferArgument(thrower, args); |
| 242 | if (buffer.start == nullptr) return; |
| 243 | |
| 244 | i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null(); |
| 245 | if (args.Length() > 2 && args[2]->IsArrayBuffer()) { |
| 246 | Local<Object> obj = Local<Object>::Cast(args[2]); |
| 247 | i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj); |
| 248 | memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj)); |
| 249 | } |
| 250 | |
| 251 | // Decode but avoid a redundant pass over function bodies for verification. |
| 252 | // Verification will happen during compilation. |
| 253 | i::Zone zone; |
| 254 | internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( |
| 255 | isolate, &zone, buffer.start, buffer.end, false, false); |
| 256 | |
| 257 | if (result.failed()) { |
| 258 | thrower.Failed("", result); |
| 259 | } else { |
| 260 | // Success. Instantiate the module and return the object. |
| 261 | i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null(); |
| 262 | if (args.Length() > 1 && args[1]->IsObject()) { |
| 263 | Local<Object> obj = Local<Object>::Cast(args[1]); |
| 264 | ffi = i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj)); |
| 265 | } |
| 266 | |
| 267 | i::MaybeHandle<i::JSObject> object = |
| 268 | result.val->Instantiate(isolate, ffi, memory); |
| 269 | |
| 270 | if (!object.is_null()) { |
| 271 | args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked())); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | if (result.val) delete result.val; |
| 276 | } |
| 277 | } // namespace |
| 278 | |
| 279 | |
| 280 | // TODO(titzer): we use the API to create the function template because the |
| 281 | // internal guts are too ugly to replicate here. |
| 282 | static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, |
| 283 | FunctionCallback func) { |
| 284 | Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate); |
| 285 | Local<FunctionTemplate> local = FunctionTemplate::New(isolate, func); |
| 286 | return v8::Utils::OpenHandle(*local); |
| 287 | } |
| 288 | |
| 289 | |
| 290 | namespace internal { |
| 291 | static Handle<String> v8_str(Isolate* isolate, const char* str) { |
| 292 | return isolate->factory()->NewStringFromAsciiChecked(str); |
| 293 | } |
| 294 | |
| 295 | |
| 296 | static void InstallFunc(Isolate* isolate, Handle<JSObject> object, |
| 297 | const char* str, FunctionCallback func) { |
| 298 | Handle<String> name = v8_str(isolate, str); |
| 299 | Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); |
| 300 | Handle<JSFunction> function = |
| 301 | ApiNatives::InstantiateFunction(temp).ToHandleChecked(); |
| 302 | PropertyAttributes attributes = |
| 303 | static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY); |
| 304 | JSObject::AddProperty(object, name, function, attributes); |
| 305 | } |
| 306 | |
| 307 | |
| 308 | void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) { |
| 309 | // Setup wasm function map. |
| 310 | Handle<Context> context(global->native_context(), isolate); |
| 311 | InstallWasmFunctionMap(isolate, context); |
| 312 | |
| 313 | // Bind the WASM object. |
| 314 | Factory* factory = isolate->factory(); |
| 315 | Handle<String> name = v8_str(isolate, "_WASMEXP_"); |
| 316 | Handle<JSFunction> cons = factory->NewFunction(name); |
| 317 | JSFunction::SetInstancePrototype( |
| 318 | cons, Handle<Object>(context->initial_object_prototype(), isolate)); |
| 319 | cons->shared()->set_instance_class_name(*name); |
| 320 | Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED); |
| 321 | PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM); |
| 322 | JSObject::AddProperty(global, name, wasm_object, attributes); |
| 323 | |
| 324 | // Install functions on the WASM object. |
| 325 | InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule); |
| 326 | InstallFunc(isolate, wasm_object, "verifyModule", VerifyModule); |
| 327 | InstallFunc(isolate, wasm_object, "verifyFunction", VerifyFunction); |
| 328 | InstallFunc(isolate, wasm_object, "compileRun", CompileRun); |
| 329 | InstallFunc(isolate, wasm_object, "asmCompileRun", AsmCompileRun); |
| 330 | InstallFunc(isolate, wasm_object, "instantiateModuleFromAsm", |
| 331 | InstantiateModuleFromAsm); |
| 332 | } |
| 333 | |
| 334 | |
| 335 | void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) { |
| 336 | if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) { |
| 337 | Handle<Map> wasm_function_map = isolate->factory()->NewMap( |
| 338 | JS_FUNCTION_TYPE, JSFunction::kSize + kPointerSize); |
| 339 | wasm_function_map->set_is_callable(); |
| 340 | context->set_wasm_function_map(*wasm_function_map); |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | } // namespace internal |
| 345 | } // namespace v8 |