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 346