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-natives.h" 6 #include "src/api.h" 7 #include "src/assert-scope.h" 8 #include "src/ast/ast.h" 9 #include "src/ast/scopes.h" 10 #include "src/execution.h" 11 #include "src/factory.h" 12 #include "src/handles.h" 13 #include "src/isolate.h" 14 #include "src/objects.h" 15 #include "src/parsing/parser.h" 16 #include "src/typing-asm.h" 17 18 #include "src/wasm/asm-wasm-builder.h" 19 #include "src/wasm/encoder.h" 20 #include "src/wasm/module-decoder.h" 21 #include "src/wasm/wasm-js.h" 22 #include "src/wasm/wasm-module.h" 23 #include "src/wasm/wasm-result.h" 24 25 typedef uint8_t byte; 26 27 using v8::internal::wasm::ErrorThrower; 28 29 namespace v8 { 30 31 namespace { 32 struct RawBuffer { 33 const byte* start; 34 const byte* end; 35 size_t size() { return static_cast<size_t>(end - start); } 36 }; 37 38 RawBuffer GetRawBufferSource( 39 v8::Local<v8::Value> source, ErrorThrower* thrower) { 40 const byte* start = nullptr; 41 const byte* end = nullptr; 42 43 if (source->IsArrayBuffer()) { 44 // A raw array buffer was passed. 45 Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source); 46 ArrayBuffer::Contents contents = buffer->GetContents(); 47 48 start = reinterpret_cast<const byte*>(contents.Data()); 49 end = start + contents.ByteLength(); 50 51 if (start == nullptr || end == start) { 52 thrower->Error("ArrayBuffer argument is empty"); 53 } 54 } else if (source->IsTypedArray()) { 55 // A TypedArray was passed. 56 Local<TypedArray> array = Local<TypedArray>::Cast(source); 57 Local<ArrayBuffer> buffer = array->Buffer(); 58 59 ArrayBuffer::Contents contents = buffer->GetContents(); 60 61 start = 62 reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset(); 63 end = start + array->ByteLength(); 64 65 if (start == nullptr || end == start) { 66 thrower->Error("ArrayBuffer argument is empty"); 67 } 68 } else { 69 thrower->Error("Argument 0 must be an ArrayBuffer or Uint8Array"); 70 } 71 72 return {start, end}; 73 } 74 75 void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { 76 HandleScope scope(args.GetIsolate()); 77 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); 78 ErrorThrower thrower(isolate, "Wasm.verifyModule()"); 79 80 if (args.Length() < 1) { 81 thrower.Error("Argument 0 must be a buffer source"); 82 return; 83 } 84 RawBuffer buffer = GetRawBufferSource(args[0], &thrower); 85 if (thrower.error()) return; 86 87 i::Zone zone(isolate->allocator()); 88 internal::wasm::ModuleResult result = 89 internal::wasm::DecodeWasmModule(isolate, &zone, buffer.start, buffer.end, 90 true, internal::wasm::kWasmOrigin); 91 92 if (result.failed()) { 93 thrower.Failed("", result); 94 } 95 96 if (result.val) delete result.val; 97 } 98 99 void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { 100 HandleScope scope(args.GetIsolate()); 101 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); 102 ErrorThrower thrower(isolate, "Wasm.verifyFunction()"); 103 104 if (args.Length() < 1) { 105 thrower.Error("Argument 0 must be a buffer source"); 106 return; 107 } 108 RawBuffer buffer = GetRawBufferSource(args[0], &thrower); 109 if (thrower.error()) return; 110 111 internal::wasm::FunctionResult result; 112 { 113 // Verification of a single function shouldn't allocate. 114 i::DisallowHeapAllocation no_allocation; 115 i::Zone zone(isolate->allocator()); 116 result = internal::wasm::DecodeWasmFunction(isolate, &zone, nullptr, 117 buffer.start, buffer.end); 118 } 119 120 if (result.failed()) { 121 thrower.Failed("", result); 122 } 123 124 if (result.val) delete result.val; 125 } 126 127 v8::internal::wasm::ZoneBuffer* TranslateAsmModule( 128 i::ParseInfo* info, ErrorThrower* thrower, 129 i::Handle<i::FixedArray>* foreign_args) { 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 if (info->scope()->declarations()->length() == 0) { 140 thrower->Error("Asm.js validation failed: no declarations in scope"); 141 return nullptr; 142 } 143 144 info->set_literal( 145 info->scope()->declarations()->at(0)->AsFunctionDeclaration()->fun()); 146 147 v8::internal::AsmTyper typer(info->isolate(), info->zone(), *(info->script()), 148 info->literal()); 149 if (i::FLAG_enable_simd_asmjs) { 150 typer.set_allow_simd(true); 151 } 152 if (!typer.Validate()) { 153 thrower->Error("Asm.js validation failed: %s", typer.error_message()); 154 return nullptr; 155 } 156 157 v8::internal::wasm::AsmWasmBuilder builder(info->isolate(), info->zone(), 158 info->literal(), &typer); 159 160 return builder.Run(foreign_args); 161 } 162 163 i::MaybeHandle<i::JSObject> InstantiateModuleCommon( 164 const v8::FunctionCallbackInfo<v8::Value>& args, const byte* start, 165 const byte* end, ErrorThrower* thrower, 166 internal::wasm::ModuleOrigin origin = i::wasm::kWasmOrigin) { 167 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); 168 169 // Decode but avoid a redundant pass over function bodies for verification. 170 // Verification will happen during compilation. 171 i::Zone zone(isolate->allocator()); 172 internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( 173 isolate, &zone, start, end, false, origin); 174 175 i::MaybeHandle<i::JSObject> object; 176 if (result.failed() && origin == internal::wasm::kAsmJsOrigin) { 177 thrower->Error("Asm.js converted module failed to decode"); 178 } else if (result.failed()) { 179 thrower->Failed("", result); 180 } else { 181 // Success. Instantiate the module and return the object. 182 i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null(); 183 if (args.Length() > 1 && args[1]->IsObject()) { 184 Local<Object> obj = Local<Object>::Cast(args[1]); 185 ffi = i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj)); 186 } 187 188 i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null(); 189 if (args.Length() > 2 && args[2]->IsArrayBuffer()) { 190 Local<Object> obj = Local<Object>::Cast(args[2]); 191 i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj); 192 memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj)); 193 } 194 195 object = result.val->Instantiate(isolate, ffi, memory); 196 if (!object.is_null()) { 197 args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked())); 198 } 199 } 200 201 if (result.val) delete result.val; 202 return object; 203 } 204 205 void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) { 206 HandleScope scope(args.GetIsolate()); 207 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); 208 ErrorThrower thrower(isolate, "Wasm.instantiateModuleFromAsm()"); 209 210 if (!args[0]->IsString()) { 211 thrower.Error("Asm module text should be a string"); 212 return; 213 } 214 215 i::Factory* factory = isolate->factory(); 216 i::Zone zone(isolate->allocator()); 217 Local<String> source = Local<String>::Cast(args[0]); 218 i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source)); 219 i::ParseInfo info(&zone, script); 220 221 i::Handle<i::Object> foreign; 222 if (args.Length() > 1 && args[1]->IsObject()) { 223 Local<Object> local_foreign = Local<Object>::Cast(args[1]); 224 foreign = v8::Utils::OpenHandle(*local_foreign); 225 } 226 227 i::Handle<i::FixedArray> foreign_args; 228 auto module = TranslateAsmModule(&info, &thrower, &foreign_args); 229 if (module == nullptr) { 230 return; 231 } 232 233 i::MaybeHandle<i::Object> maybe_module_object = 234 InstantiateModuleCommon(args, module->begin(), module->end(), &thrower, 235 internal::wasm::kAsmJsOrigin); 236 if (maybe_module_object.is_null()) { 237 return; 238 } 239 240 i::Handle<i::Name> name = 241 factory->NewStringFromStaticChars("__foreign_init__"); 242 243 i::Handle<i::Object> module_object = maybe_module_object.ToHandleChecked(); 244 i::MaybeHandle<i::Object> maybe_init = 245 i::Object::GetProperty(module_object, name); 246 DCHECK(!maybe_init.is_null()); 247 248 i::Handle<i::Object> init = maybe_init.ToHandleChecked(); 249 i::Handle<i::Object> undefined = isolate->factory()->undefined_value(); 250 i::Handle<i::Object>* foreign_args_array = 251 new i::Handle<i::Object>[foreign_args->length()]; 252 for (int j = 0; j < foreign_args->length(); j++) { 253 if (!foreign.is_null()) { 254 i::MaybeHandle<i::Name> name = i::Object::ToName( 255 isolate, i::Handle<i::Object>(foreign_args->get(j), isolate)); 256 if (!name.is_null()) { 257 i::MaybeHandle<i::Object> val = 258 i::Object::GetProperty(foreign, name.ToHandleChecked()); 259 if (!val.is_null()) { 260 foreign_args_array[j] = val.ToHandleChecked(); 261 continue; 262 } 263 } 264 } 265 foreign_args_array[j] = undefined; 266 } 267 i::MaybeHandle<i::Object> retval = i::Execution::Call( 268 isolate, init, undefined, foreign_args->length(), foreign_args_array); 269 delete[] foreign_args_array; 270 271 if (retval.is_null()) { 272 thrower.Error( 273 "WASM.instantiateModuleFromAsm(): foreign init function failed"); 274 } 275 } 276 277 void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) { 278 HandleScope scope(args.GetIsolate()); 279 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); 280 ErrorThrower thrower(isolate, "Wasm.instantiateModule()"); 281 282 if (args.Length() < 1) { 283 thrower.Error("Argument 0 must be a buffer source"); 284 return; 285 } 286 RawBuffer buffer = GetRawBufferSource(args[0], &thrower); 287 if (buffer.start == nullptr) return; 288 289 InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower); 290 } 291 292 293 static i::MaybeHandle<i::JSObject> CreateModuleObject( 294 v8::Isolate* isolate, const v8::Local<v8::Value> source, 295 ErrorThrower* thrower) { 296 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 297 298 RawBuffer buffer = GetRawBufferSource(source, thrower); 299 if (buffer.start == nullptr) return i::MaybeHandle<i::JSObject>(); 300 301 // TODO(rossberg): Once we can, do compilation here. 302 DCHECK(source->IsArrayBuffer() || source->IsTypedArray()); 303 Local<Context> context = isolate->GetCurrentContext(); 304 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 305 i::Handle<i::JSFunction> module_cons(i_context->wasm_module_constructor()); 306 i::Handle<i::JSObject> module_obj = 307 i_isolate->factory()->NewJSObject(module_cons); 308 i::Handle<i::Object> module_ref = Utils::OpenHandle(*source); 309 i::Handle<i::Symbol> module_sym(i_context->wasm_module_sym()); 310 i::Object::SetProperty(module_obj, module_sym, module_ref, i::STRICT).Check(); 311 312 return module_obj; 313 } 314 315 void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) { 316 v8::Isolate* isolate = args.GetIsolate(); 317 HandleScope scope(isolate); 318 ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), 319 "WebAssembly.compile()"); 320 321 if (args.Length() < 1) { 322 thrower.Error("Argument 0 must be a buffer source"); 323 return; 324 } 325 i::MaybeHandle<i::JSObject> module_obj = 326 CreateModuleObject(isolate, args[0], &thrower); 327 if (module_obj.is_null()) return; 328 329 Local<Context> context = isolate->GetCurrentContext(); 330 v8::Local<v8::Promise::Resolver> resolver; 331 if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return; 332 resolver->Resolve(context, Utils::ToLocal(module_obj.ToHandleChecked())); 333 334 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 335 return_value.Set(resolver->GetPromise()); 336 } 337 338 void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { 339 v8::Isolate* isolate = args.GetIsolate(); 340 HandleScope scope(isolate); 341 ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), 342 "WebAssembly.Module()"); 343 344 if (args.Length() < 1) { 345 thrower.Error("Argument 0 must be a buffer source"); 346 return; 347 } 348 i::MaybeHandle<i::JSObject> module_obj = 349 CreateModuleObject(isolate, args[0], &thrower); 350 if (module_obj.is_null()) return; 351 352 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 353 return_value.Set(Utils::ToLocal(module_obj.ToHandleChecked())); 354 } 355 356 void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) { 357 HandleScope scope(args.GetIsolate()); 358 v8::Isolate* isolate = args.GetIsolate(); 359 ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), 360 "WebAssembly.Instance()"); 361 362 if (args.Length() < 1) { 363 thrower.Error("Argument 0 must be a WebAssembly.Module"); 364 return; 365 } 366 Local<Context> context = isolate->GetCurrentContext(); 367 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 368 i::Handle<i::Symbol> module_sym(i_context->wasm_module_sym()); 369 i::MaybeHandle<i::Object> source = 370 i::Object::GetProperty(Utils::OpenHandle(*args[0]), module_sym); 371 if (source.is_null()) return; 372 373 RawBuffer buffer = 374 GetRawBufferSource(Utils::ToLocal(source.ToHandleChecked()), &thrower); 375 if (buffer.start == nullptr) return; 376 377 InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower); 378 } 379 } // namespace 380 381 // TODO(titzer): we use the API to create the function template because the 382 // internal guts are too ugly to replicate here. 383 static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, 384 FunctionCallback func) { 385 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate); 386 Local<FunctionTemplate> local = FunctionTemplate::New(isolate, func); 387 return v8::Utils::OpenHandle(*local); 388 } 389 390 namespace internal { 391 static Handle<String> v8_str(Isolate* isolate, const char* str) { 392 return isolate->factory()->NewStringFromAsciiChecked(str); 393 } 394 395 static Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object, 396 const char* str, FunctionCallback func) { 397 Handle<String> name = v8_str(isolate, str); 398 Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); 399 Handle<JSFunction> function = 400 ApiNatives::InstantiateFunction(temp).ToHandleChecked(); 401 PropertyAttributes attributes = 402 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY); 403 JSObject::AddProperty(object, name, function, attributes); 404 return function; 405 } 406 407 void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) { 408 Factory* factory = isolate->factory(); 409 410 // Setup wasm function map. 411 Handle<Context> context(global->native_context(), isolate); 412 InstallWasmFunctionMap(isolate, context); 413 414 // Bind the experimental WASM object. 415 // TODO(rossberg, titzer): remove once it's no longer needed. 416 { 417 Handle<String> name = v8_str(isolate, "Wasm"); 418 Handle<JSFunction> cons = factory->NewFunction(name); 419 JSFunction::SetInstancePrototype( 420 cons, Handle<Object>(context->initial_object_prototype(), isolate)); 421 cons->shared()->set_instance_class_name(*name); 422 Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED); 423 PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM); 424 JSObject::AddProperty(global, name, wasm_object, attributes); 425 426 // Install functions on the WASM object. 427 InstallFunc(isolate, wasm_object, "verifyModule", VerifyModule); 428 InstallFunc(isolate, wasm_object, "verifyFunction", VerifyFunction); 429 InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule); 430 InstallFunc(isolate, wasm_object, "instantiateModuleFromAsm", 431 InstantiateModuleFromAsm); 432 433 { 434 // Add the Wasm.experimentalVersion property. 435 Handle<String> name = v8_str(isolate, "experimentalVersion"); 436 PropertyAttributes attributes = 437 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY); 438 Handle<Smi> value = 439 Handle<Smi>(Smi::FromInt(wasm::kWasmVersion), isolate); 440 JSObject::AddProperty(wasm_object, name, value, attributes); 441 } 442 } 443 444 // Create private symbols. 445 Handle<Symbol> module_sym = isolate->factory()->NewPrivateSymbol(); 446 Handle<Symbol> instance_sym = isolate->factory()->NewPrivateSymbol(); 447 context->set_wasm_module_sym(*module_sym); 448 context->set_wasm_instance_sym(*instance_sym); 449 450 // Bind the WebAssembly object. 451 Handle<String> name = v8_str(isolate, "WebAssembly"); 452 Handle<JSFunction> cons = factory->NewFunction(name); 453 JSFunction::SetInstancePrototype( 454 cons, Handle<Object>(context->initial_object_prototype(), isolate)); 455 cons->shared()->set_instance_class_name(*name); 456 Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED); 457 PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM); 458 JSObject::AddProperty(global, name, wasm_object, attributes); 459 460 // Install static methods on WebAssembly object. 461 InstallFunc(isolate, wasm_object, "compile", WebAssemblyCompile); 462 Handle<JSFunction> module_constructor = 463 InstallFunc(isolate, wasm_object, "Module", WebAssemblyModule); 464 Handle<JSFunction> instance_constructor = 465 InstallFunc(isolate, wasm_object, "Instance", WebAssemblyInstance); 466 context->set_wasm_module_constructor(*module_constructor); 467 context->set_wasm_instance_constructor(*instance_constructor); 468 } 469 470 void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) { 471 if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) { 472 // TODO(titzer): Move this to bootstrapper.cc?? 473 // TODO(titzer): Also make one for strict mode functions? 474 Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate); 475 476 InstanceType instance_type = prev_map->instance_type(); 477 int internal_fields = JSObject::GetInternalFieldCount(*prev_map); 478 CHECK_EQ(0, internal_fields); 479 int pre_allocated = 480 prev_map->GetInObjectProperties() - prev_map->unused_property_fields(); 481 int instance_size; 482 int in_object_properties; 483 JSFunction::CalculateInstanceSizeHelper(instance_type, internal_fields + 1, 484 0, &instance_size, 485 &in_object_properties); 486 487 int unused_property_fields = in_object_properties - pre_allocated; 488 Handle<Map> map = Map::CopyInitialMap( 489 prev_map, instance_size, in_object_properties, unused_property_fields); 490 491 context->set_wasm_function_map(*map); 492 } 493 } 494 495 } // namespace internal 496 } // namespace v8 497