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/asmjs/asm-js.h" 8 #include "src/asmjs/asm-typer.h" 9 #include "src/asmjs/asm-wasm-builder.h" 10 #include "src/assert-scope.h" 11 #include "src/ast/ast.h" 12 #include "src/execution.h" 13 #include "src/factory.h" 14 #include "src/handles.h" 15 #include "src/isolate.h" 16 #include "src/objects-inl.h" 17 #include "src/objects.h" 18 #include "src/parsing/parse-info.h" 19 20 #include "src/wasm/module-decoder.h" 21 #include "src/wasm/wasm-js.h" 22 #include "src/wasm/wasm-limits.h" 23 #include "src/wasm/wasm-module.h" 24 #include "src/wasm/wasm-objects.h" 25 #include "src/wasm/wasm-result.h" 26 27 typedef uint8_t byte; 28 29 using v8::internal::wasm::ErrorThrower; 30 31 namespace v8 { 32 33 namespace { 34 35 #define RANGE_ERROR_MSG \ 36 "Wasm compilation exceeds internal limits in this context for the provided " \ 37 "arguments" 38 39 // TODO(wasm): move brand check to the respective types, and don't throw 40 // in it, rather, use a provided ErrorThrower, or let caller handle it. 41 static bool HasBrand(i::Handle<i::Object> value, i::Handle<i::Symbol> sym) { 42 if (!value->IsJSObject()) return false; 43 i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value); 44 Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym); 45 return has_brand.FromMaybe(false); 46 } 47 48 static bool BrandCheck(i::Handle<i::Object> value, i::Handle<i::Symbol> sym, 49 ErrorThrower* thrower, const char* msg) { 50 return HasBrand(value, sym) ? true : (thrower->TypeError("%s", msg), false); 51 } 52 53 i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) { 54 return isolate->factory()->NewStringFromAsciiChecked(str); 55 } 56 Local<String> v8_str(Isolate* isolate, const char* str) { 57 return Utils::ToLocal(v8_str(reinterpret_cast<i::Isolate*>(isolate), str)); 58 } 59 60 i::MaybeHandle<i::WasmModuleObject> GetFirstArgumentAsModule( 61 const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) { 62 v8::Isolate* isolate = args.GetIsolate(); 63 if (args.Length() < 1) { 64 thrower->TypeError("Argument 0 must be a WebAssembly.Module"); 65 return {}; 66 } 67 68 Local<Context> context = isolate->GetCurrentContext(); 69 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 70 if (!BrandCheck(Utils::OpenHandle(*args[0]), 71 i::handle(i_context->wasm_module_sym()), thrower, 72 "Argument 0 must be a WebAssembly.Module")) { 73 return {}; 74 } 75 76 Local<Object> module_obj = Local<Object>::Cast(args[0]); 77 return i::Handle<i::WasmModuleObject>::cast( 78 v8::Utils::OpenHandle(*module_obj)); 79 } 80 81 bool IsCompilationAllowed(i::Isolate* isolate, ErrorThrower* thrower, 82 v8::Local<v8::Value> source, bool is_async) { 83 // Allow caller to do one final check on thrower state, rather than 84 // one at each step. No information is lost - failure reason is captured 85 // in the thrower state. 86 if (thrower->error()) return false; 87 88 AllowWasmCompileCallback callback = isolate->allow_wasm_compile_callback(); 89 if (callback != nullptr && 90 !callback(reinterpret_cast<v8::Isolate*>(isolate), source, is_async)) { 91 thrower->RangeError(RANGE_ERROR_MSG); 92 return false; 93 } 94 return true; 95 } 96 97 bool IsInstantiationAllowed(i::Isolate* isolate, ErrorThrower* thrower, 98 v8::Local<v8::Value> module_or_bytes, 99 i::MaybeHandle<i::JSReceiver> ffi, bool is_async) { 100 // Allow caller to do one final check on thrower state, rather than 101 // one at each step. No information is lost - failure reason is captured 102 // in the thrower state. 103 if (thrower->error()) return false; 104 v8::MaybeLocal<v8::Value> v8_ffi; 105 if (!ffi.is_null()) { 106 v8_ffi = v8::Local<v8::Value>::Cast(Utils::ToLocal(ffi.ToHandleChecked())); 107 } 108 AllowWasmInstantiateCallback callback = 109 isolate->allow_wasm_instantiate_callback(); 110 if (callback != nullptr && 111 !callback(reinterpret_cast<v8::Isolate*>(isolate), module_or_bytes, 112 v8_ffi, is_async)) { 113 thrower->RangeError(RANGE_ERROR_MSG); 114 return false; 115 } 116 return true; 117 } 118 119 i::wasm::ModuleWireBytes GetFirstArgumentAsBytes( 120 const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) { 121 if (args.Length() < 1) { 122 thrower->TypeError("Argument 0 must be a buffer source"); 123 return i::wasm::ModuleWireBytes(nullptr, nullptr); 124 } 125 126 const byte* start = nullptr; 127 size_t length = 0; 128 v8::Local<v8::Value> source = args[0]; 129 if (source->IsArrayBuffer()) { 130 // A raw array buffer was passed. 131 Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source); 132 ArrayBuffer::Contents contents = buffer->GetContents(); 133 134 start = reinterpret_cast<const byte*>(contents.Data()); 135 length = contents.ByteLength(); 136 } else if (source->IsTypedArray()) { 137 // A TypedArray was passed. 138 Local<TypedArray> array = Local<TypedArray>::Cast(source); 139 Local<ArrayBuffer> buffer = array->Buffer(); 140 141 ArrayBuffer::Contents contents = buffer->GetContents(); 142 143 start = 144 reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset(); 145 length = array->ByteLength(); 146 } else { 147 thrower->TypeError("Argument 0 must be a buffer source"); 148 } 149 DCHECK_IMPLIES(length, start != nullptr); 150 if (length == 0) { 151 thrower->CompileError("BufferSource argument is empty"); 152 } 153 if (length > i::wasm::kV8MaxWasmModuleSize) { 154 thrower->RangeError("buffer source exceeds maximum size of %zu (is %zu)", 155 i::wasm::kV8MaxWasmModuleSize, length); 156 } 157 if (thrower->error()) return i::wasm::ModuleWireBytes(nullptr, nullptr); 158 // TODO(titzer): use the handle as well? 159 return i::wasm::ModuleWireBytes(start, start + length); 160 } 161 162 i::MaybeHandle<i::JSReceiver> GetSecondArgumentAsImports( 163 const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) { 164 if (args.Length() < 2) return {}; 165 if (args[1]->IsUndefined()) return {}; 166 167 if (!args[1]->IsObject()) { 168 thrower->TypeError("Argument 1 must be an object"); 169 return {}; 170 } 171 Local<Object> obj = Local<Object>::Cast(args[1]); 172 return i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj)); 173 } 174 175 // WebAssembly.compile(bytes) -> Promise 176 void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) { 177 v8::Isolate* isolate = args.GetIsolate(); 178 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 179 HandleScope scope(isolate); 180 ErrorThrower thrower(i_isolate, "WebAssembly.compile()"); 181 182 Local<Context> context = isolate->GetCurrentContext(); 183 v8::Local<v8::Promise::Resolver> resolver; 184 if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return; 185 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 186 return_value.Set(resolver->GetPromise()); 187 188 auto bytes = GetFirstArgumentAsBytes(args, &thrower); 189 if (!IsCompilationAllowed(i_isolate, &thrower, args[0], true)) { 190 resolver->Reject(context, Utils::ToLocal(thrower.Reify())); 191 return; 192 } 193 DCHECK(!thrower.error()); 194 i::Handle<i::JSPromise> promise = Utils::OpenHandle(*resolver->GetPromise()); 195 i::wasm::AsyncCompile(i_isolate, promise, bytes); 196 } 197 198 // WebAssembly.validate(bytes) -> bool 199 void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) { 200 v8::Isolate* isolate = args.GetIsolate(); 201 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 202 HandleScope scope(isolate); 203 ErrorThrower thrower(i_isolate, "WebAssembly.validate()"); 204 205 auto bytes = GetFirstArgumentAsBytes(args, &thrower); 206 207 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 208 if (!thrower.error() && 209 i::wasm::SyncValidate(reinterpret_cast<i::Isolate*>(isolate), &thrower, 210 bytes)) { 211 return_value.Set(v8::True(isolate)); 212 } else { 213 if (thrower.wasm_error()) thrower.Reify(); // Clear error. 214 return_value.Set(v8::False(isolate)); 215 } 216 } 217 218 // new WebAssembly.Module(bytes) -> WebAssembly.Module 219 void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { 220 v8::Isolate* isolate = args.GetIsolate(); 221 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 222 HandleScope scope(isolate); 223 ErrorThrower thrower(i_isolate, "WebAssembly.Module()"); 224 225 auto bytes = GetFirstArgumentAsBytes(args, &thrower); 226 if (!IsCompilationAllowed(i_isolate, &thrower, args[0], false)) return; 227 228 DCHECK(!thrower.error()); 229 i::MaybeHandle<i::Object> module_obj = 230 i::wasm::SyncCompile(i_isolate, &thrower, bytes); 231 if (module_obj.is_null()) return; 232 233 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 234 return_value.Set(Utils::ToLocal(module_obj.ToHandleChecked())); 235 } 236 237 // WebAssembly.Module.imports(module) -> Array<Import> 238 void WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value>& args) { 239 HandleScope scope(args.GetIsolate()); 240 v8::Isolate* isolate = args.GetIsolate(); 241 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 242 ErrorThrower thrower(i_isolate, "WebAssembly.Module.imports()"); 243 244 auto maybe_module = GetFirstArgumentAsModule(args, &thrower); 245 if (thrower.error()) return; 246 auto imports = i::wasm::GetImports(i_isolate, maybe_module.ToHandleChecked()); 247 args.GetReturnValue().Set(Utils::ToLocal(imports)); 248 } 249 250 // WebAssembly.Module.exports(module) -> Array<Export> 251 void WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value>& args) { 252 HandleScope scope(args.GetIsolate()); 253 v8::Isolate* isolate = args.GetIsolate(); 254 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 255 ErrorThrower thrower(i_isolate, "WebAssembly.Module.exports()"); 256 257 auto maybe_module = GetFirstArgumentAsModule(args, &thrower); 258 if (thrower.error()) return; 259 auto exports = i::wasm::GetExports(i_isolate, maybe_module.ToHandleChecked()); 260 args.GetReturnValue().Set(Utils::ToLocal(exports)); 261 } 262 263 // WebAssembly.Module.customSections(module, name) -> Array<Section> 264 void WebAssemblyModuleCustomSections( 265 const v8::FunctionCallbackInfo<v8::Value>& args) { 266 HandleScope scope(args.GetIsolate()); 267 v8::Isolate* isolate = args.GetIsolate(); 268 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 269 ErrorThrower thrower(i_isolate, "WebAssembly.Module.customSections()"); 270 271 auto maybe_module = GetFirstArgumentAsModule(args, &thrower); 272 if (thrower.error()) return; 273 274 if (args.Length() < 2) { 275 thrower.TypeError("Argument 1 must be a string"); 276 return; 277 } 278 279 i::Handle<i::Object> name = Utils::OpenHandle(*args[1]); 280 if (!name->IsString()) { 281 thrower.TypeError("Argument 1 must be a string"); 282 return; 283 } 284 285 auto custom_sections = 286 i::wasm::GetCustomSections(i_isolate, maybe_module.ToHandleChecked(), 287 i::Handle<i::String>::cast(name), &thrower); 288 if (thrower.error()) return; 289 args.GetReturnValue().Set(Utils::ToLocal(custom_sections)); 290 } 291 292 // new WebAssembly.Instance(module, imports) -> WebAssembly.Instance 293 void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) { 294 HandleScope scope(args.GetIsolate()); 295 v8::Isolate* isolate = args.GetIsolate(); 296 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 297 ErrorThrower thrower(i_isolate, "WebAssembly.Instance()"); 298 299 auto maybe_module = GetFirstArgumentAsModule(args, &thrower); 300 if (thrower.error()) return; 301 302 auto maybe_imports = GetSecondArgumentAsImports(args, &thrower); 303 if (!IsInstantiationAllowed(i_isolate, &thrower, args[0], maybe_imports, 304 false)) { 305 return; 306 } 307 DCHECK(!thrower.error()); 308 309 i::MaybeHandle<i::Object> instance_object = i::wasm::SyncInstantiate( 310 i_isolate, &thrower, maybe_module.ToHandleChecked(), maybe_imports, 311 i::MaybeHandle<i::JSArrayBuffer>()); 312 if (instance_object.is_null()) return; 313 args.GetReturnValue().Set(Utils::ToLocal(instance_object.ToHandleChecked())); 314 } 315 316 // WebAssembly.instantiate(module, imports) -> WebAssembly.Instance 317 // WebAssembly.instantiate(bytes, imports) -> 318 // {module: WebAssembly.Module, instance: WebAssembly.Instance} 319 void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) { 320 v8::Isolate* isolate = args.GetIsolate(); 321 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 322 ErrorThrower thrower(i_isolate, "WebAssembly.instantiate()"); 323 324 HandleScope scope(isolate); 325 326 Local<Context> context = isolate->GetCurrentContext(); 327 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 328 329 v8::Local<v8::Promise::Resolver> resolver; 330 if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return; 331 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 332 return_value.Set(resolver->GetPromise()); 333 334 if (args.Length() < 1) { 335 thrower.TypeError( 336 "Argument 0 must be provided and must be either a buffer source or a " 337 "WebAssembly.Module object"); 338 resolver->Reject(context, Utils::ToLocal(thrower.Reify())); 339 return; 340 } 341 342 i::Handle<i::Object> first_arg = Utils::OpenHandle(*args[0]); 343 if (!first_arg->IsJSObject()) { 344 thrower.TypeError( 345 "Argument 0 must be a buffer source or a WebAssembly.Module object"); 346 resolver->Reject(context, Utils::ToLocal(thrower.Reify())); 347 return; 348 } 349 350 auto maybe_imports = GetSecondArgumentAsImports(args, &thrower); 351 if (thrower.error()) { 352 resolver->Reject(context, Utils::ToLocal(thrower.Reify())); 353 return; 354 } 355 if (!IsInstantiationAllowed(i_isolate, &thrower, args[0], maybe_imports, 356 true)) { 357 resolver->Reject(context, Utils::ToLocal(thrower.Reify())); 358 return; 359 } 360 i::Handle<i::JSPromise> promise = Utils::OpenHandle(*resolver->GetPromise()); 361 if (HasBrand(first_arg, i::Handle<i::Symbol>(i_context->wasm_module_sym()))) { 362 // WebAssembly.instantiate(module, imports) -> WebAssembly.Instance 363 auto module_object = GetFirstArgumentAsModule(args, &thrower); 364 i::wasm::AsyncInstantiate(i_isolate, promise, 365 module_object.ToHandleChecked(), maybe_imports); 366 } else { 367 // WebAssembly.instantiate(bytes, imports) -> {module, instance} 368 auto bytes = GetFirstArgumentAsBytes(args, &thrower); 369 if (thrower.error()) { 370 resolver->Reject(context, Utils::ToLocal(thrower.Reify())); 371 return; 372 } 373 i::wasm::AsyncCompileAndInstantiate(i_isolate, promise, bytes, 374 maybe_imports); 375 } 376 } 377 378 bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower, 379 Local<Context> context, Local<v8::Object> object, 380 Local<String> property, int* result, 381 int64_t lower_bound, uint64_t upper_bound) { 382 v8::MaybeLocal<v8::Value> maybe = object->Get(context, property); 383 v8::Local<v8::Value> value; 384 if (maybe.ToLocal(&value)) { 385 int64_t number; 386 if (!value->IntegerValue(context).To(&number)) return false; 387 if (number < lower_bound) { 388 thrower->RangeError("Property value %" PRId64 389 " is below the lower bound %" PRIx64, 390 number, lower_bound); 391 return false; 392 } 393 if (number > static_cast<int64_t>(upper_bound)) { 394 thrower->RangeError("Property value %" PRId64 395 " is above the upper bound %" PRIu64, 396 number, upper_bound); 397 return false; 398 } 399 *result = static_cast<int>(number); 400 return true; 401 } 402 return false; 403 } 404 405 // new WebAssembly.Table(args) -> WebAssembly.Table 406 void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) { 407 v8::Isolate* isolate = args.GetIsolate(); 408 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 409 HandleScope scope(isolate); 410 ErrorThrower thrower(i_isolate, "WebAssembly.Module()"); 411 if (args.Length() < 1 || !args[0]->IsObject()) { 412 thrower.TypeError("Argument 0 must be a table descriptor"); 413 return; 414 } 415 Local<Context> context = isolate->GetCurrentContext(); 416 Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked(); 417 // The descriptor's 'element'. 418 { 419 v8::MaybeLocal<v8::Value> maybe = 420 descriptor->Get(context, v8_str(isolate, "element")); 421 v8::Local<v8::Value> value; 422 if (!maybe.ToLocal(&value)) return; 423 v8::Local<v8::String> string; 424 if (!value->ToString(context).ToLocal(&string)) return; 425 bool equal; 426 if (!string->Equals(context, v8_str(isolate, "anyfunc")).To(&equal)) return; 427 if (!equal) { 428 thrower.TypeError("Descriptor property 'element' must be 'anyfunc'"); 429 return; 430 } 431 } 432 // The descriptor's 'initial'. 433 int initial = 0; 434 if (!GetIntegerProperty(isolate, &thrower, context, descriptor, 435 v8_str(isolate, "initial"), &initial, 0, 436 i::FLAG_wasm_max_table_size)) { 437 return; 438 } 439 // The descriptor's 'maximum'. 440 int maximum = -1; 441 Local<String> maximum_key = v8_str(isolate, "maximum"); 442 Maybe<bool> has_maximum = descriptor->Has(context, maximum_key); 443 444 if (!has_maximum.IsNothing() && has_maximum.FromJust()) { 445 if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key, 446 &maximum, initial, 447 i::wasm::kSpecMaxWasmTableSize)) { 448 return; 449 } 450 } 451 452 i::Handle<i::FixedArray> fixed_array; 453 i::Handle<i::JSObject> table_obj = 454 i::WasmTableObject::New(i_isolate, initial, maximum, &fixed_array); 455 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 456 return_value.Set(Utils::ToLocal(table_obj)); 457 } 458 459 void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) { 460 v8::Isolate* isolate = args.GetIsolate(); 461 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 462 HandleScope scope(isolate); 463 ErrorThrower thrower(i_isolate, "WebAssembly.Memory()"); 464 if (args.Length() < 1 || !args[0]->IsObject()) { 465 thrower.TypeError("Argument 0 must be a memory descriptor"); 466 return; 467 } 468 Local<Context> context = isolate->GetCurrentContext(); 469 Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked(); 470 // The descriptor's 'initial'. 471 int initial = 0; 472 if (!GetIntegerProperty(isolate, &thrower, context, descriptor, 473 v8_str(isolate, "initial"), &initial, 0, 474 i::FLAG_wasm_max_mem_pages)) { 475 return; 476 } 477 // The descriptor's 'maximum'. 478 int maximum = -1; 479 Local<String> maximum_key = v8_str(isolate, "maximum"); 480 Maybe<bool> has_maximum = descriptor->Has(context, maximum_key); 481 482 if (!has_maximum.IsNothing() && has_maximum.FromJust()) { 483 if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key, 484 &maximum, initial, 485 i::wasm::kSpecMaxWasmMemoryPages)) { 486 return; 487 } 488 } 489 size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) * 490 static_cast<size_t>(initial); 491 i::Handle<i::JSArrayBuffer> buffer = 492 i::wasm::NewArrayBuffer(i_isolate, size, i::FLAG_wasm_guard_pages); 493 if (buffer.is_null()) { 494 thrower.RangeError("could not allocate memory"); 495 return; 496 } 497 i::Handle<i::JSObject> memory_obj = 498 i::WasmMemoryObject::New(i_isolate, buffer, maximum); 499 args.GetReturnValue().Set(Utils::ToLocal(memory_obj)); 500 } 501 502 void WebAssemblyTableGetLength( 503 const v8::FunctionCallbackInfo<v8::Value>& args) { 504 v8::Isolate* isolate = args.GetIsolate(); 505 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 506 HandleScope scope(isolate); 507 ErrorThrower thrower(i_isolate, "WebAssembly.Table.length()"); 508 Local<Context> context = isolate->GetCurrentContext(); 509 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 510 if (!BrandCheck(Utils::OpenHandle(*args.This()), 511 i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower, 512 "Receiver is not a WebAssembly.Table")) { 513 return; 514 } 515 auto receiver = 516 i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); 517 args.GetReturnValue().Set( 518 v8::Number::New(isolate, receiver->current_length())); 519 } 520 521 // WebAssembly.Table.grow(num) -> num 522 void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { 523 v8::Isolate* isolate = args.GetIsolate(); 524 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 525 HandleScope scope(isolate); 526 ErrorThrower thrower(i_isolate, "WebAssembly.Table.grow()"); 527 Local<Context> context = isolate->GetCurrentContext(); 528 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 529 if (!BrandCheck(Utils::OpenHandle(*args.This()), 530 i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower, 531 "Receiver is not a WebAssembly.Table")) { 532 return; 533 } 534 535 auto receiver = 536 i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); 537 i::Handle<i::FixedArray> old_array(receiver->functions(), i_isolate); 538 int old_size = old_array->length(); 539 int64_t new_size64 = 0; 540 if (args.Length() > 0 && !args[0]->IntegerValue(context).To(&new_size64)) { 541 return; 542 } 543 new_size64 += old_size; 544 545 int64_t max_size64 = receiver->maximum_length(); 546 if (max_size64 < 0 || 547 max_size64 > static_cast<int64_t>(i::FLAG_wasm_max_table_size)) { 548 max_size64 = i::FLAG_wasm_max_table_size; 549 } 550 551 if (new_size64 < old_size || new_size64 > max_size64) { 552 thrower.RangeError(new_size64 < old_size ? "trying to shrink table" 553 : "maximum table size exceeded"); 554 return; 555 } 556 557 int new_size = static_cast<int>(new_size64); 558 i::WasmTableObject::Grow(i_isolate, receiver, 559 static_cast<uint32_t>(new_size - old_size)); 560 561 if (new_size != old_size) { 562 i::Handle<i::FixedArray> new_array = 563 i_isolate->factory()->NewFixedArray(new_size); 564 for (int i = 0; i < old_size; ++i) new_array->set(i, old_array->get(i)); 565 i::Object* null = i_isolate->heap()->null_value(); 566 for (int i = old_size; i < new_size; ++i) new_array->set(i, null); 567 receiver->set_functions(*new_array); 568 } 569 570 // TODO(gdeepti): use weak links for instances 571 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 572 return_value.Set(old_size); 573 } 574 575 // WebAssembly.Table.get(num) -> JSFunction 576 void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) { 577 v8::Isolate* isolate = args.GetIsolate(); 578 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 579 HandleScope scope(isolate); 580 ErrorThrower thrower(i_isolate, "WebAssembly.Table.get()"); 581 Local<Context> context = isolate->GetCurrentContext(); 582 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 583 if (!BrandCheck(Utils::OpenHandle(*args.This()), 584 i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower, 585 "Receiver is not a WebAssembly.Table")) { 586 return; 587 } 588 589 auto receiver = 590 i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); 591 i::Handle<i::FixedArray> array(receiver->functions(), i_isolate); 592 int i = 0; 593 if (args.Length() > 0 && !args[0]->Int32Value(context).To(&i)) return; 594 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 595 if (i < 0 || i >= array->length()) { 596 thrower.RangeError("index out of bounds"); 597 return; 598 } 599 600 i::Handle<i::Object> value(array->get(i), i_isolate); 601 return_value.Set(Utils::ToLocal(value)); 602 } 603 604 // WebAssembly.Table.set(num, JSFunction) 605 void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) { 606 v8::Isolate* isolate = args.GetIsolate(); 607 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 608 HandleScope scope(isolate); 609 ErrorThrower thrower(i_isolate, "WebAssembly.Table.set()"); 610 Local<Context> context = isolate->GetCurrentContext(); 611 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 612 if (!BrandCheck(Utils::OpenHandle(*args.This()), 613 i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower, 614 "Receiver is not a WebAssembly.Table")) { 615 return; 616 } 617 if (args.Length() < 2) { 618 thrower.TypeError("Argument 1 must be null or a function"); 619 return; 620 } 621 i::Handle<i::Object> value = Utils::OpenHandle(*args[1]); 622 if (!value->IsNull(i_isolate) && 623 (!value->IsJSFunction() || 624 i::Handle<i::JSFunction>::cast(value)->code()->kind() != 625 i::Code::JS_TO_WASM_FUNCTION)) { 626 thrower.TypeError("Argument 1 must be null or a WebAssembly function"); 627 return; 628 } 629 630 auto receiver = 631 i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); 632 i::Handle<i::FixedArray> array(receiver->functions(), i_isolate); 633 int i; 634 if (!args[0]->Int32Value(context).To(&i)) return; 635 if (i < 0 || i >= array->length()) { 636 thrower.RangeError("index out of bounds"); 637 return; 638 } 639 640 i::Handle<i::FixedArray> dispatch_tables(receiver->dispatch_tables(), 641 i_isolate); 642 if (value->IsNull(i_isolate)) { 643 i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i, 644 i::Handle<i::JSFunction>::null()); 645 } else { 646 i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i, 647 i::Handle<i::JSFunction>::cast(value)); 648 } 649 650 i::Handle<i::FixedArray>::cast(array)->set(i, *value); 651 } 652 653 // WebAssembly.Memory.grow(num) -> num 654 void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { 655 v8::Isolate* isolate = args.GetIsolate(); 656 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 657 HandleScope scope(isolate); 658 ErrorThrower thrower(i_isolate, "WebAssembly.Memory.grow()"); 659 Local<Context> context = isolate->GetCurrentContext(); 660 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 661 if (!BrandCheck(Utils::OpenHandle(*args.This()), 662 i::Handle<i::Symbol>(i_context->wasm_memory_sym()), &thrower, 663 "Receiver is not a WebAssembly.Memory")) { 664 return; 665 } 666 int64_t delta_size = 0; 667 if (args.Length() < 1 || !args[0]->IntegerValue(context).To(&delta_size)) { 668 thrower.TypeError("Argument 0 required, must be numeric value of pages"); 669 return; 670 } 671 i::Handle<i::WasmMemoryObject> receiver = 672 i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This())); 673 int64_t max_size64 = receiver->maximum_pages(); 674 if (max_size64 < 0 || 675 max_size64 > static_cast<int64_t>(i::FLAG_wasm_max_mem_pages)) { 676 max_size64 = i::FLAG_wasm_max_mem_pages; 677 } 678 i::Handle<i::JSArrayBuffer> old_buffer(receiver->buffer()); 679 uint32_t old_size = 680 old_buffer->byte_length()->Number() / i::wasm::kSpecMaxWasmMemoryPages; 681 int64_t new_size64 = old_size + delta_size; 682 if (delta_size < 0 || max_size64 < new_size64 || new_size64 < old_size) { 683 thrower.RangeError(new_size64 < old_size ? "trying to shrink memory" 684 : "maximum memory size exceeded"); 685 return; 686 } 687 int32_t ret = i::wasm::GrowWebAssemblyMemory( 688 i_isolate, receiver, static_cast<uint32_t>(delta_size)); 689 if (ret == -1) { 690 thrower.RangeError("Unable to grow instance memory."); 691 return; 692 } 693 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 694 return_value.Set(ret); 695 } 696 697 // WebAssembly.Memory.buffer -> ArrayBuffer 698 void WebAssemblyMemoryGetBuffer( 699 const v8::FunctionCallbackInfo<v8::Value>& args) { 700 v8::Isolate* isolate = args.GetIsolate(); 701 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 702 HandleScope scope(isolate); 703 ErrorThrower thrower(i_isolate, "WebAssembly.Memory.buffer"); 704 Local<Context> context = isolate->GetCurrentContext(); 705 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 706 if (!BrandCheck(Utils::OpenHandle(*args.This()), 707 i::Handle<i::Symbol>(i_context->wasm_memory_sym()), &thrower, 708 "Receiver is not a WebAssembly.Memory")) { 709 return; 710 } 711 i::Handle<i::WasmMemoryObject> receiver = 712 i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This())); 713 i::Handle<i::Object> buffer(receiver->buffer(), i_isolate); 714 DCHECK(buffer->IsJSArrayBuffer()); 715 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 716 return_value.Set(Utils::ToLocal(buffer)); 717 } 718 } // namespace 719 720 // TODO(titzer): we use the API to create the function template because the 721 // internal guts are too ugly to replicate here. 722 static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, 723 FunctionCallback func) { 724 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate); 725 Local<FunctionTemplate> templ = FunctionTemplate::New(isolate, func); 726 templ->ReadOnlyPrototype(); 727 return v8::Utils::OpenHandle(*templ); 728 } 729 730 namespace internal { 731 732 Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object, 733 const char* str, FunctionCallback func, 734 int length = 0) { 735 Handle<String> name = v8_str(isolate, str); 736 Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); 737 Handle<JSFunction> function = 738 ApiNatives::InstantiateFunction(temp).ToHandleChecked(); 739 JSFunction::SetName(function, name, isolate->factory()->empty_string()); 740 function->shared()->set_length(length); 741 PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM); 742 JSObject::AddProperty(object, name, function, attributes); 743 return function; 744 } 745 746 Handle<JSFunction> InstallGetter(Isolate* isolate, Handle<JSObject> object, 747 const char* str, FunctionCallback func) { 748 Handle<String> name = v8_str(isolate, str); 749 Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); 750 Handle<JSFunction> function = 751 ApiNatives::InstantiateFunction(temp).ToHandleChecked(); 752 v8::PropertyAttribute attributes = 753 static_cast<v8::PropertyAttribute>(v8::DontEnum); 754 Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name), 755 Utils::ToLocal(function), 756 Local<Function>(), attributes); 757 return function; 758 } 759 760 void WasmJs::Install(Isolate* isolate) { 761 Handle<JSGlobalObject> global = isolate->global_object(); 762 Handle<Context> context(global->native_context(), isolate); 763 // TODO(titzer): once FLAG_expose_wasm is gone, this should become a DCHECK. 764 if (context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) return; 765 766 // Install Maps. 767 768 // TODO(titzer): Also make one for strict mode functions? 769 Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate); 770 771 InstanceType instance_type = prev_map->instance_type(); 772 int internal_fields = JSObject::GetInternalFieldCount(*prev_map); 773 CHECK_EQ(0, internal_fields); 774 int pre_allocated = 775 prev_map->GetInObjectProperties() - prev_map->unused_property_fields(); 776 int instance_size = 0; 777 int in_object_properties = 0; 778 int wasm_internal_fields = internal_fields + 1 // module instance object 779 + 1 // function arity 780 + 1; // function signature 781 JSFunction::CalculateInstanceSizeHelper(instance_type, wasm_internal_fields, 782 0, &instance_size, 783 &in_object_properties); 784 785 int unused_property_fields = in_object_properties - pre_allocated; 786 Handle<Map> map = Map::CopyInitialMap( 787 prev_map, instance_size, in_object_properties, unused_property_fields); 788 789 context->set_wasm_function_map(*map); 790 791 // Install symbols. 792 793 Factory* factory = isolate->factory(); 794 // Create private symbols. 795 Handle<Symbol> module_sym = factory->NewPrivateSymbol(); 796 context->set_wasm_module_sym(*module_sym); 797 798 Handle<Symbol> instance_sym = factory->NewPrivateSymbol(); 799 context->set_wasm_instance_sym(*instance_sym); 800 801 Handle<Symbol> table_sym = factory->NewPrivateSymbol(); 802 context->set_wasm_table_sym(*table_sym); 803 804 Handle<Symbol> memory_sym = factory->NewPrivateSymbol(); 805 context->set_wasm_memory_sym(*memory_sym); 806 807 // Install the JS API. 808 809 // Setup WebAssembly 810 Handle<String> name = v8_str(isolate, "WebAssembly"); 811 Handle<JSFunction> cons = factory->NewFunction(name); 812 JSFunction::SetInstancePrototype( 813 cons, Handle<Object>(context->initial_object_prototype(), isolate)); 814 cons->shared()->set_instance_class_name(*name); 815 Handle<JSObject> webassembly = factory->NewJSObject(cons, TENURED); 816 PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM); 817 JSObject::AddProperty(global, name, webassembly, attributes); 818 PropertyAttributes ro_attributes = 819 static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY); 820 JSObject::AddProperty(webassembly, factory->to_string_tag_symbol(), 821 v8_str(isolate, "WebAssembly"), ro_attributes); 822 InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile, 1); 823 InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate, 1); 824 InstallFunc(isolate, webassembly, "instantiate", WebAssemblyInstantiate, 1); 825 826 // Setup Module 827 Handle<JSFunction> module_constructor = 828 InstallFunc(isolate, webassembly, "Module", WebAssemblyModule, 1); 829 context->set_wasm_module_constructor(*module_constructor); 830 Handle<JSObject> module_proto = 831 factory->NewJSObject(module_constructor, TENURED); 832 i::Handle<i::Map> module_map = isolate->factory()->NewMap( 833 i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize + 834 WasmModuleObject::kFieldCount * i::kPointerSize); 835 JSFunction::SetInitialMap(module_constructor, module_map, module_proto); 836 InstallFunc(isolate, module_constructor, "imports", WebAssemblyModuleImports, 837 1); 838 InstallFunc(isolate, module_constructor, "exports", WebAssemblyModuleExports, 839 1); 840 InstallFunc(isolate, module_constructor, "customSections", 841 WebAssemblyModuleCustomSections, 2); 842 JSObject::AddProperty(module_proto, isolate->factory()->constructor_string(), 843 module_constructor, DONT_ENUM); 844 JSObject::AddProperty(module_proto, factory->to_string_tag_symbol(), 845 v8_str(isolate, "WebAssembly.Module"), ro_attributes); 846 847 // Setup Instance 848 Handle<JSFunction> instance_constructor = 849 InstallFunc(isolate, webassembly, "Instance", WebAssemblyInstance, 1); 850 context->set_wasm_instance_constructor(*instance_constructor); 851 Handle<JSObject> instance_proto = 852 factory->NewJSObject(instance_constructor, TENURED); 853 i::Handle<i::Map> instance_map = isolate->factory()->NewMap( 854 i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize + 855 WasmInstanceObject::kFieldCount * i::kPointerSize); 856 JSFunction::SetInitialMap(instance_constructor, instance_map, instance_proto); 857 JSObject::AddProperty(instance_proto, 858 isolate->factory()->constructor_string(), 859 instance_constructor, DONT_ENUM); 860 JSObject::AddProperty(instance_proto, factory->to_string_tag_symbol(), 861 v8_str(isolate, "WebAssembly.Instance"), ro_attributes); 862 863 // Setup Table 864 Handle<JSFunction> table_constructor = 865 InstallFunc(isolate, webassembly, "Table", WebAssemblyTable, 1); 866 context->set_wasm_table_constructor(*table_constructor); 867 Handle<JSObject> table_proto = 868 factory->NewJSObject(table_constructor, TENURED); 869 i::Handle<i::Map> table_map = isolate->factory()->NewMap( 870 i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize + 871 WasmTableObject::kFieldCount * i::kPointerSize); 872 JSFunction::SetInitialMap(table_constructor, table_map, table_proto); 873 JSObject::AddProperty(table_proto, isolate->factory()->constructor_string(), 874 table_constructor, DONT_ENUM); 875 InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength); 876 InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow, 1); 877 InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet, 1); 878 InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet, 2); 879 JSObject::AddProperty(table_proto, factory->to_string_tag_symbol(), 880 v8_str(isolate, "WebAssembly.Table"), ro_attributes); 881 882 // Setup Memory 883 Handle<JSFunction> memory_constructor = 884 InstallFunc(isolate, webassembly, "Memory", WebAssemblyMemory, 1); 885 context->set_wasm_memory_constructor(*memory_constructor); 886 Handle<JSObject> memory_proto = 887 factory->NewJSObject(memory_constructor, TENURED); 888 i::Handle<i::Map> memory_map = isolate->factory()->NewMap( 889 i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize + 890 WasmMemoryObject::kFieldCount * i::kPointerSize); 891 JSFunction::SetInitialMap(memory_constructor, memory_map, memory_proto); 892 JSObject::AddProperty(memory_proto, isolate->factory()->constructor_string(), 893 memory_constructor, DONT_ENUM); 894 InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow, 1); 895 InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer); 896 JSObject::AddProperty(memory_proto, factory->to_string_tag_symbol(), 897 v8_str(isolate, "WebAssembly.Memory"), ro_attributes); 898 899 // Setup errors 900 attributes = static_cast<PropertyAttributes>(DONT_ENUM); 901 Handle<JSFunction> compile_error( 902 isolate->native_context()->wasm_compile_error_function()); 903 JSObject::AddProperty(webassembly, isolate->factory()->CompileError_string(), 904 compile_error, attributes); 905 Handle<JSFunction> link_error( 906 isolate->native_context()->wasm_link_error_function()); 907 JSObject::AddProperty(webassembly, isolate->factory()->LinkError_string(), 908 link_error, attributes); 909 Handle<JSFunction> runtime_error( 910 isolate->native_context()->wasm_runtime_error_function()); 911 JSObject::AddProperty(webassembly, isolate->factory()->RuntimeError_string(), 912 runtime_error, attributes); 913 } 914 915 bool WasmJs::IsWasmMemoryObject(Isolate* isolate, Handle<Object> value) { 916 i::Handle<i::Symbol> symbol(isolate->context()->wasm_memory_sym(), isolate); 917 return HasBrand(value, symbol); 918 } 919 920 bool WasmJs::IsWasmTableObject(Isolate* isolate, Handle<Object> value) { 921 i::Handle<i::Symbol> symbol(isolate->context()->wasm_table_sym(), isolate); 922 return HasBrand(value, symbol); 923 } 924 } // namespace internal 925 } // namespace v8 926