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.h" 17 #include "src/parsing/parse-info.h" 18 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-objects.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 enum WasmMemoryObjectData { 32 kWasmMemoryBuffer, 33 kWasmMemoryMaximum, 34 kWasmMemoryInstanceObject 35 }; 36 37 namespace { 38 i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) { 39 return isolate->factory()->NewStringFromAsciiChecked(str); 40 } 41 Local<String> v8_str(Isolate* isolate, const char* str) { 42 return Utils::ToLocal(v8_str(reinterpret_cast<i::Isolate*>(isolate), str)); 43 } 44 45 struct RawBuffer { 46 const byte* start; 47 const byte* end; 48 size_t size() { return static_cast<size_t>(end - start); } 49 }; 50 51 RawBuffer GetRawBufferSource( 52 v8::Local<v8::Value> source, ErrorThrower* thrower) { 53 const byte* start = nullptr; 54 const byte* end = nullptr; 55 56 if (source->IsArrayBuffer()) { 57 // A raw array buffer was passed. 58 Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source); 59 ArrayBuffer::Contents contents = buffer->GetContents(); 60 61 start = reinterpret_cast<const byte*>(contents.Data()); 62 end = start + contents.ByteLength(); 63 64 if (start == nullptr || end == start) { 65 thrower->CompileError("ArrayBuffer argument is empty"); 66 } 67 } else if (source->IsTypedArray()) { 68 // A TypedArray was passed. 69 Local<TypedArray> array = Local<TypedArray>::Cast(source); 70 Local<ArrayBuffer> buffer = array->Buffer(); 71 72 ArrayBuffer::Contents contents = buffer->GetContents(); 73 74 start = 75 reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset(); 76 end = start + array->ByteLength(); 77 78 if (start == nullptr || end == start) { 79 thrower->TypeError("ArrayBuffer argument is empty"); 80 } 81 } else { 82 thrower->TypeError("Argument 0 must be an ArrayBuffer or Uint8Array"); 83 } 84 85 return {start, end}; 86 } 87 88 static i::MaybeHandle<i::WasmModuleObject> CreateModuleObject( 89 v8::Isolate* isolate, const v8::Local<v8::Value> source, 90 ErrorThrower* thrower) { 91 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 92 i::MaybeHandle<i::JSObject> nothing; 93 94 RawBuffer buffer = GetRawBufferSource(source, thrower); 95 if (buffer.start == nullptr) return i::MaybeHandle<i::WasmModuleObject>(); 96 97 DCHECK(source->IsArrayBuffer() || source->IsTypedArray()); 98 return i::wasm::CreateModuleObjectFromBytes( 99 i_isolate, buffer.start, buffer.end, thrower, i::wasm::kWasmOrigin, 100 i::Handle<i::Script>::null(), nullptr, nullptr); 101 } 102 103 static bool ValidateModule(v8::Isolate* isolate, 104 const v8::Local<v8::Value> source, 105 ErrorThrower* thrower) { 106 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 107 i::MaybeHandle<i::JSObject> nothing; 108 109 RawBuffer buffer = GetRawBufferSource(source, thrower); 110 if (buffer.start == nullptr) return false; 111 112 DCHECK(source->IsArrayBuffer() || source->IsTypedArray()); 113 return i::wasm::ValidateModuleBytes(i_isolate, buffer.start, buffer.end, 114 thrower, 115 i::wasm::ModuleOrigin::kWasmOrigin); 116 } 117 118 static bool BrandCheck(Isolate* isolate, i::Handle<i::Object> value, 119 i::Handle<i::Symbol> sym, const char* msg) { 120 if (value->IsJSObject()) { 121 i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value); 122 Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym); 123 if (has_brand.IsNothing()) return false; 124 if (has_brand.ToChecked()) return true; 125 } 126 v8::Local<v8::Value> e = v8::Exception::TypeError(v8_str(isolate, msg)); 127 isolate->ThrowException(e); 128 return false; 129 } 130 131 void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) { 132 v8::Isolate* isolate = args.GetIsolate(); 133 HandleScope scope(isolate); 134 ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), 135 "WebAssembly.compile()"); 136 137 if (args.Length() < 1) { 138 thrower.TypeError("Argument 0 must be a buffer source"); 139 return; 140 } 141 i::MaybeHandle<i::JSObject> module_obj = 142 CreateModuleObject(isolate, args[0], &thrower); 143 144 Local<Context> context = isolate->GetCurrentContext(); 145 v8::Local<v8::Promise::Resolver> resolver; 146 if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return; 147 if (thrower.error()) { 148 resolver->Reject(context, Utils::ToLocal(thrower.Reify())); 149 } else { 150 resolver->Resolve(context, Utils::ToLocal(module_obj.ToHandleChecked())); 151 } 152 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 153 return_value.Set(resolver->GetPromise()); 154 } 155 156 void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) { 157 v8::Isolate* isolate = args.GetIsolate(); 158 HandleScope scope(isolate); 159 ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), 160 "WebAssembly.validate()"); 161 162 if (args.Length() < 1) { 163 thrower.TypeError("Argument 0 must be a buffer source"); 164 return; 165 } 166 167 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 168 if (ValidateModule(isolate, args[0], &thrower)) { 169 return_value.Set(v8::True(isolate)); 170 } else { 171 return_value.Set(v8::False(isolate)); 172 } 173 } 174 175 void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { 176 v8::Isolate* isolate = args.GetIsolate(); 177 HandleScope scope(isolate); 178 ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), 179 "WebAssembly.Module()"); 180 181 if (args.Length() < 1) { 182 thrower.TypeError("Argument 0 must be a buffer source"); 183 return; 184 } 185 i::MaybeHandle<i::JSObject> module_obj = 186 CreateModuleObject(isolate, args[0], &thrower); 187 if (module_obj.is_null()) return; 188 189 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 190 return_value.Set(Utils::ToLocal(module_obj.ToHandleChecked())); 191 } 192 193 void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) { 194 HandleScope scope(args.GetIsolate()); 195 v8::Isolate* isolate = args.GetIsolate(); 196 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 197 198 ErrorThrower thrower(i_isolate, "WebAssembly.Instance()"); 199 200 if (args.Length() < 1) { 201 thrower.TypeError("Argument 0 must be a WebAssembly.Module"); 202 return; 203 } 204 205 Local<Context> context = isolate->GetCurrentContext(); 206 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 207 if (!BrandCheck(isolate, Utils::OpenHandle(*args[0]), 208 i::Handle<i::Symbol>(i_context->wasm_module_sym()), 209 "Argument 0 must be a WebAssembly.Module")) { 210 return; 211 } 212 213 Local<Object> obj = Local<Object>::Cast(args[0]); 214 i::Handle<i::JSObject> i_obj = 215 i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj)); 216 217 i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null(); 218 if (args.Length() > 1 && args[1]->IsObject()) { 219 Local<Object> obj = Local<Object>::Cast(args[1]); 220 ffi = i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj)); 221 } 222 223 i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null(); 224 if (args.Length() > 2 && args[2]->IsObject()) { 225 Local<Object> obj = Local<Object>::Cast(args[2]); 226 i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj); 227 if (i::WasmJs::IsWasmMemoryObject(i_isolate, mem_obj)) { 228 memory = i::Handle<i::JSArrayBuffer>( 229 i::Handle<i::WasmMemoryObject>::cast(mem_obj)->get_buffer(), 230 i_isolate); 231 } else { 232 thrower.TypeError("Argument 2 must be a WebAssembly.Memory"); 233 } 234 } 235 i::MaybeHandle<i::JSObject> instance = 236 i::wasm::WasmModule::Instantiate(i_isolate, &thrower, i_obj, ffi, memory); 237 if (instance.is_null()) { 238 if (!thrower.error()) thrower.RuntimeError("Could not instantiate module"); 239 return; 240 } 241 DCHECK(!i_isolate->has_pending_exception()); 242 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 243 return_value.Set(Utils::ToLocal(instance.ToHandleChecked())); 244 } 245 246 bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower, 247 Local<Context> context, Local<v8::Object> object, 248 Local<String> property, int* result, int lower_bound, 249 int upper_bound) { 250 v8::MaybeLocal<v8::Value> maybe = object->Get(context, property); 251 v8::Local<v8::Value> value; 252 if (maybe.ToLocal(&value)) { 253 int64_t number; 254 if (!value->IntegerValue(context).To(&number)) return false; 255 if (number < static_cast<int64_t>(lower_bound)) { 256 thrower->RangeError("Property value %" PRId64 257 " is below the lower bound %d", 258 number, lower_bound); 259 return false; 260 } 261 if (number > static_cast<int64_t>(upper_bound)) { 262 thrower->RangeError("Property value %" PRId64 263 " is above the upper bound %d", 264 number, upper_bound); 265 return false; 266 } 267 *result = static_cast<int>(number); 268 return true; 269 } 270 return false; 271 } 272 273 const int max_table_size = 1 << 26; 274 275 void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) { 276 v8::Isolate* isolate = args.GetIsolate(); 277 HandleScope scope(isolate); 278 ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), 279 "WebAssembly.Module()"); 280 if (args.Length() < 1 || !args[0]->IsObject()) { 281 thrower.TypeError("Argument 0 must be a table descriptor"); 282 return; 283 } 284 Local<Context> context = isolate->GetCurrentContext(); 285 Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked(); 286 // The descriptor's 'element'. 287 { 288 v8::MaybeLocal<v8::Value> maybe = 289 descriptor->Get(context, v8_str(isolate, "element")); 290 v8::Local<v8::Value> value; 291 if (!maybe.ToLocal(&value)) return; 292 v8::Local<v8::String> string; 293 if (!value->ToString(context).ToLocal(&string)) return; 294 bool equal; 295 if (!string->Equals(context, v8_str(isolate, "anyfunc")).To(&equal)) return; 296 if (!equal) { 297 thrower.TypeError("Descriptor property 'element' must be 'anyfunc'"); 298 return; 299 } 300 } 301 // The descriptor's 'initial'. 302 int initial; 303 if (!GetIntegerProperty(isolate, &thrower, context, descriptor, 304 v8_str(isolate, "initial"), &initial, 0, 305 max_table_size)) { 306 return; 307 } 308 // The descriptor's 'maximum'. 309 int maximum = 0; 310 Local<String> maximum_key = v8_str(isolate, "maximum"); 311 Maybe<bool> has_maximum = descriptor->Has(context, maximum_key); 312 313 if (has_maximum.IsNothing()) { 314 // There has been an exception, just return. 315 return; 316 } 317 if (has_maximum.FromJust()) { 318 if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key, 319 &maximum, initial, max_table_size)) { 320 return; 321 } 322 } else { 323 maximum = static_cast<int>(i::wasm::WasmModule::kV8MaxTableSize); 324 } 325 326 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 327 i::Handle<i::FixedArray> fixed_array; 328 i::Handle<i::JSObject> table_obj = 329 i::WasmTableObject::New(i_isolate, initial, maximum, &fixed_array); 330 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 331 return_value.Set(Utils::ToLocal(table_obj)); 332 } 333 334 void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) { 335 v8::Isolate* isolate = args.GetIsolate(); 336 HandleScope scope(isolate); 337 ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), 338 "WebAssembly.Module()"); 339 if (args.Length() < 1 || !args[0]->IsObject()) { 340 thrower.TypeError("Argument 0 must be a memory descriptor"); 341 return; 342 } 343 Local<Context> context = isolate->GetCurrentContext(); 344 Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked(); 345 // The descriptor's 'initial'. 346 int initial; 347 if (!GetIntegerProperty(isolate, &thrower, context, descriptor, 348 v8_str(isolate, "initial"), &initial, 0, 65536)) { 349 return; 350 } 351 // The descriptor's 'maximum'. 352 int maximum = 0; 353 Local<String> maximum_key = v8_str(isolate, "maximum"); 354 Maybe<bool> has_maximum = descriptor->Has(context, maximum_key); 355 356 if (has_maximum.IsNothing()) { 357 // There has been an exception, just return. 358 return; 359 } 360 if (has_maximum.FromJust()) { 361 if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key, 362 &maximum, initial, 65536)) { 363 return; 364 } 365 } 366 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 367 i::Handle<i::JSArrayBuffer> buffer = 368 i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared); 369 size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) * 370 static_cast<size_t>(initial); 371 i::JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, size); 372 373 i::Handle<i::JSObject> memory_obj = i::WasmMemoryObject::New( 374 i_isolate, buffer, has_maximum.FromJust() ? maximum : -1); 375 args.GetReturnValue().Set(Utils::ToLocal(memory_obj)); 376 } 377 378 void WebAssemblyTableGetLength( 379 const v8::FunctionCallbackInfo<v8::Value>& args) { 380 v8::Isolate* isolate = args.GetIsolate(); 381 Local<Context> context = isolate->GetCurrentContext(); 382 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 383 if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()), 384 i::Handle<i::Symbol>(i_context->wasm_table_sym()), 385 "Receiver is not a WebAssembly.Table")) { 386 return; 387 } 388 auto receiver = 389 i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); 390 args.GetReturnValue().Set( 391 v8::Number::New(isolate, receiver->current_length())); 392 } 393 394 void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { 395 v8::Isolate* isolate = args.GetIsolate(); 396 Local<Context> context = isolate->GetCurrentContext(); 397 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 398 if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()), 399 i::Handle<i::Symbol>(i_context->wasm_table_sym()), 400 "Receiver is not a WebAssembly.Table")) { 401 return; 402 } 403 404 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 405 auto receiver = 406 i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); 407 i::Handle<i::FixedArray> old_array(receiver->get_functions(), i_isolate); 408 int old_size = old_array->length(); 409 int64_t new_size64 = 0; 410 if (args.Length() > 0 && !args[0]->IntegerValue(context).To(&new_size64)) { 411 return; 412 } 413 new_size64 += old_size; 414 415 if (new_size64 < old_size || new_size64 > receiver->maximum_length()) { 416 v8::Local<v8::Value> e = v8::Exception::RangeError( 417 v8_str(isolate, new_size64 < old_size ? "trying to shrink table" 418 : "maximum table size exceeded")); 419 isolate->ThrowException(e); 420 return; 421 } 422 int new_size = static_cast<int>(new_size64); 423 424 if (new_size != old_size) { 425 i::Handle<i::FixedArray> new_array = 426 i_isolate->factory()->NewFixedArray(new_size); 427 for (int i = 0; i < old_size; ++i) new_array->set(i, old_array->get(i)); 428 i::Object* null = i_isolate->heap()->null_value(); 429 for (int i = old_size; i < new_size; ++i) new_array->set(i, null); 430 receiver->set_functions(*new_array); 431 } 432 433 // TODO(titzer): update relevant instances. 434 } 435 436 void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) { 437 v8::Isolate* isolate = args.GetIsolate(); 438 Local<Context> context = isolate->GetCurrentContext(); 439 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 440 if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()), 441 i::Handle<i::Symbol>(i_context->wasm_table_sym()), 442 "Receiver is not a WebAssembly.Table")) { 443 return; 444 } 445 446 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 447 auto receiver = 448 i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); 449 i::Handle<i::FixedArray> array(receiver->get_functions(), i_isolate); 450 int i = 0; 451 if (args.Length() > 0 && !args[0]->Int32Value(context).To(&i)) return; 452 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 453 if (i < 0 || i >= array->length()) { 454 v8::Local<v8::Value> e = 455 v8::Exception::RangeError(v8_str(isolate, "index out of bounds")); 456 isolate->ThrowException(e); 457 return; 458 } 459 460 i::Handle<i::Object> value(array->get(i), i_isolate); 461 return_value.Set(Utils::ToLocal(value)); 462 } 463 464 void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) { 465 v8::Isolate* isolate = args.GetIsolate(); 466 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 467 Local<Context> context = isolate->GetCurrentContext(); 468 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 469 if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()), 470 i::Handle<i::Symbol>(i_context->wasm_table_sym()), 471 "Receiver is not a WebAssembly.Table")) { 472 return; 473 } 474 if (args.Length() < 2) { 475 v8::Local<v8::Value> e = v8::Exception::TypeError( 476 v8_str(isolate, "Argument 1 must be null or a function")); 477 isolate->ThrowException(e); 478 return; 479 } 480 i::Handle<i::Object> value = Utils::OpenHandle(*args[1]); 481 if (!value->IsNull(i_isolate) && 482 (!value->IsJSFunction() || 483 i::Handle<i::JSFunction>::cast(value)->code()->kind() != 484 i::Code::JS_TO_WASM_FUNCTION)) { 485 v8::Local<v8::Value> e = v8::Exception::TypeError( 486 v8_str(isolate, "Argument 1 must be null or a WebAssembly function")); 487 isolate->ThrowException(e); 488 return; 489 } 490 491 auto receiver = 492 i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); 493 i::Handle<i::FixedArray> array(receiver->get_functions(), i_isolate); 494 int i; 495 if (!args[0]->Int32Value(context).To(&i)) return; 496 if (i < 0 || i >= array->length()) { 497 v8::Local<v8::Value> e = 498 v8::Exception::RangeError(v8_str(isolate, "index out of bounds")); 499 isolate->ThrowException(e); 500 return; 501 } 502 503 i::Handle<i::FixedArray> dispatch_tables(receiver->get_dispatch_tables(), 504 i_isolate); 505 if (value->IsNull(i_isolate)) { 506 i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i, 507 i::Handle<i::JSFunction>::null()); 508 } else { 509 i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i, 510 i::Handle<i::JSFunction>::cast(value)); 511 } 512 513 i::Handle<i::FixedArray>::cast(array)->set(i, *value); 514 } 515 516 void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { 517 v8::Isolate* isolate = args.GetIsolate(); 518 Local<Context> context = isolate->GetCurrentContext(); 519 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 520 if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()), 521 i::Handle<i::Symbol>(i_context->wasm_memory_sym()), 522 "Receiver is not a WebAssembly.Memory")) { 523 return; 524 } 525 if (args.Length() < 1) { 526 v8::Local<v8::Value> e = v8::Exception::TypeError( 527 v8_str(isolate, "Argument 0 required, must be numeric value of pages")); 528 isolate->ThrowException(e); 529 return; 530 } 531 532 uint32_t delta = args[0]->Uint32Value(context).FromJust(); 533 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 534 i::Handle<i::JSObject> receiver = 535 i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This())); 536 i::Handle<i::Object> instance_object( 537 receiver->GetInternalField(kWasmMemoryInstanceObject), i_isolate); 538 i::Handle<i::JSObject> instance( 539 i::Handle<i::JSObject>::cast(instance_object)); 540 541 // TODO(gdeepti) Implement growing memory when shared by different 542 // instances. 543 int32_t ret = internal::wasm::GrowInstanceMemory(i_isolate, instance, delta); 544 if (ret == -1) { 545 v8::Local<v8::Value> e = v8::Exception::Error( 546 v8_str(isolate, "Unable to grow instance memory.")); 547 isolate->ThrowException(e); 548 return; 549 } 550 i::MaybeHandle<i::JSArrayBuffer> buffer = 551 internal::wasm::GetInstanceMemory(i_isolate, instance); 552 if (buffer.is_null()) { 553 v8::Local<v8::Value> e = v8::Exception::Error( 554 v8_str(isolate, "WebAssembly.Memory buffer object not set.")); 555 isolate->ThrowException(e); 556 return; 557 } 558 receiver->SetInternalField(kWasmMemoryBuffer, *buffer.ToHandleChecked()); 559 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 560 return_value.Set(ret); 561 } 562 563 void WebAssemblyMemoryGetBuffer( 564 const v8::FunctionCallbackInfo<v8::Value>& args) { 565 v8::Isolate* isolate = args.GetIsolate(); 566 Local<Context> context = isolate->GetCurrentContext(); 567 i::Handle<i::Context> i_context = Utils::OpenHandle(*context); 568 if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()), 569 i::Handle<i::Symbol>(i_context->wasm_memory_sym()), 570 "Receiver is not a WebAssembly.Memory")) { 571 return; 572 } 573 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 574 i::Handle<i::JSObject> receiver = 575 i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This())); 576 i::Handle<i::Object> buffer(receiver->GetInternalField(kWasmMemoryBuffer), 577 i_isolate); 578 DCHECK(buffer->IsJSArrayBuffer()); 579 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); 580 return_value.Set(Utils::ToLocal(buffer)); 581 } 582 } // namespace 583 584 // TODO(titzer): we use the API to create the function template because the 585 // internal guts are too ugly to replicate here. 586 static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, 587 FunctionCallback func) { 588 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate); 589 Local<FunctionTemplate> local = FunctionTemplate::New(isolate, func); 590 return v8::Utils::OpenHandle(*local); 591 } 592 593 namespace internal { 594 595 Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object, 596 const char* str, FunctionCallback func) { 597 Handle<String> name = v8_str(isolate, str); 598 Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); 599 Handle<JSFunction> function = 600 ApiNatives::InstantiateFunction(temp).ToHandleChecked(); 601 PropertyAttributes attributes = 602 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY); 603 JSObject::AddProperty(object, name, function, attributes); 604 return function; 605 } 606 607 Handle<JSFunction> InstallGetter(Isolate* isolate, Handle<JSObject> object, 608 const char* str, FunctionCallback func) { 609 Handle<String> name = v8_str(isolate, str); 610 Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); 611 Handle<JSFunction> function = 612 ApiNatives::InstantiateFunction(temp).ToHandleChecked(); 613 v8::PropertyAttribute attributes = 614 static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly); 615 Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name), 616 Utils::ToLocal(function), 617 Local<Function>(), attributes); 618 return function; 619 } 620 621 void WasmJs::InstallWasmModuleSymbolIfNeeded(Isolate* isolate, 622 Handle<JSGlobalObject> global, 623 Handle<Context> context) { 624 if (!context->get(Context::WASM_MODULE_SYM_INDEX)->IsSymbol() || 625 !context->get(Context::WASM_INSTANCE_SYM_INDEX)->IsSymbol()) { 626 InstallWasmMapsIfNeeded(isolate, isolate->native_context()); 627 InstallWasmConstructors(isolate, isolate->global_object(), 628 isolate->native_context()); 629 } 630 } 631 632 void WasmJs::InstallWasmConstructors(Isolate* isolate, 633 Handle<JSGlobalObject> global, 634 Handle<Context> context) { 635 Factory* factory = isolate->factory(); 636 // Create private symbols. 637 Handle<Symbol> module_sym = factory->NewPrivateSymbol(); 638 context->set_wasm_module_sym(*module_sym); 639 640 Handle<Symbol> instance_sym = factory->NewPrivateSymbol(); 641 context->set_wasm_instance_sym(*instance_sym); 642 643 Handle<Symbol> table_sym = factory->NewPrivateSymbol(); 644 context->set_wasm_table_sym(*table_sym); 645 646 Handle<Symbol> memory_sym = factory->NewPrivateSymbol(); 647 context->set_wasm_memory_sym(*memory_sym); 648 649 // Bind the WebAssembly object. 650 Handle<String> name = v8_str(isolate, "WebAssembly"); 651 Handle<JSFunction> cons = factory->NewFunction(name); 652 JSFunction::SetInstancePrototype( 653 cons, Handle<Object>(context->initial_object_prototype(), isolate)); 654 cons->shared()->set_instance_class_name(*name); 655 Handle<JSObject> webassembly = factory->NewJSObject(cons, TENURED); 656 PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM); 657 JSObject::AddProperty(global, name, webassembly, attributes); 658 659 // Setup compile 660 InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile); 661 662 // Setup compile 663 InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate); 664 665 // Setup Module 666 Handle<JSFunction> module_constructor = 667 InstallFunc(isolate, webassembly, "Module", WebAssemblyModule); 668 context->set_wasm_module_constructor(*module_constructor); 669 Handle<JSObject> module_proto = 670 factory->NewJSObject(module_constructor, TENURED); 671 i::Handle<i::Map> map = isolate->factory()->NewMap( 672 i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + 673 WasmModuleObject::kFieldCount * i::kPointerSize); 674 JSFunction::SetInitialMap(module_constructor, map, module_proto); 675 JSObject::AddProperty(module_proto, isolate->factory()->constructor_string(), 676 module_constructor, DONT_ENUM); 677 678 // Setup Instance 679 Handle<JSFunction> instance_constructor = 680 InstallFunc(isolate, webassembly, "Instance", WebAssemblyInstance); 681 context->set_wasm_instance_constructor(*instance_constructor); 682 683 // Setup Table 684 Handle<JSFunction> table_constructor = 685 InstallFunc(isolate, webassembly, "Table", WebAssemblyTable); 686 context->set_wasm_table_constructor(*table_constructor); 687 Handle<JSObject> table_proto = 688 factory->NewJSObject(table_constructor, TENURED); 689 map = isolate->factory()->NewMap( 690 i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + 691 WasmTableObject::kFieldCount * i::kPointerSize); 692 JSFunction::SetInitialMap(table_constructor, map, table_proto); 693 JSObject::AddProperty(table_proto, isolate->factory()->constructor_string(), 694 table_constructor, DONT_ENUM); 695 InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength); 696 InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow); 697 InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet); 698 InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet); 699 700 // Setup Memory 701 Handle<JSFunction> memory_constructor = 702 InstallFunc(isolate, webassembly, "Memory", WebAssemblyMemory); 703 context->set_wasm_memory_constructor(*memory_constructor); 704 Handle<JSObject> memory_proto = 705 factory->NewJSObject(memory_constructor, TENURED); 706 map = isolate->factory()->NewMap( 707 i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + 708 WasmMemoryObject::kFieldCount * i::kPointerSize); 709 JSFunction::SetInitialMap(memory_constructor, map, memory_proto); 710 JSObject::AddProperty(memory_proto, isolate->factory()->constructor_string(), 711 memory_constructor, DONT_ENUM); 712 InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow); 713 InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer); 714 715 // Setup errors 716 attributes = static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY); 717 Handle<JSFunction> compile_error( 718 isolate->native_context()->wasm_compile_error_function()); 719 JSObject::AddProperty(webassembly, isolate->factory()->CompileError_string(), 720 compile_error, attributes); 721 Handle<JSFunction> runtime_error( 722 isolate->native_context()->wasm_runtime_error_function()); 723 JSObject::AddProperty(webassembly, isolate->factory()->RuntimeError_string(), 724 runtime_error, attributes); 725 } 726 727 void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) { 728 if (!FLAG_expose_wasm && !FLAG_validate_asm) { 729 return; 730 } 731 732 // Setup wasm function map. 733 Handle<Context> context(global->native_context(), isolate); 734 InstallWasmMapsIfNeeded(isolate, context); 735 736 if (FLAG_expose_wasm) { 737 InstallWasmConstructors(isolate, global, context); 738 } 739 } 740 741 void WasmJs::InstallWasmMapsIfNeeded(Isolate* isolate, 742 Handle<Context> context) { 743 if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) { 744 // TODO(titzer): Move this to bootstrapper.cc?? 745 // TODO(titzer): Also make one for strict mode functions? 746 Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate); 747 748 InstanceType instance_type = prev_map->instance_type(); 749 int internal_fields = JSObject::GetInternalFieldCount(*prev_map); 750 CHECK_EQ(0, internal_fields); 751 int pre_allocated = 752 prev_map->GetInObjectProperties() - prev_map->unused_property_fields(); 753 int instance_size = 0; 754 int in_object_properties = 0; 755 int wasm_internal_fields = internal_fields + 1 // module instance object 756 + 1 // function arity 757 + 1; // function signature 758 JSFunction::CalculateInstanceSizeHelper(instance_type, wasm_internal_fields, 759 0, &instance_size, 760 &in_object_properties); 761 762 int unused_property_fields = in_object_properties - pre_allocated; 763 Handle<Map> map = Map::CopyInitialMap( 764 prev_map, instance_size, in_object_properties, unused_property_fields); 765 766 context->set_wasm_function_map(*map); 767 } 768 } 769 770 static bool HasBrand(i::Handle<i::Object> value, i::Handle<i::Symbol> symbol) { 771 if (value->IsJSObject()) { 772 i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value); 773 Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, symbol); 774 if (has_brand.IsNothing()) return false; 775 if (has_brand.ToChecked()) return true; 776 } 777 return false; 778 } 779 780 bool WasmJs::IsWasmMemoryObject(Isolate* isolate, Handle<Object> value) { 781 i::Handle<i::Symbol> symbol(isolate->context()->wasm_memory_sym(), isolate); 782 return HasBrand(value, symbol); 783 } 784 785 bool WasmJs::IsWasmTableObject(Isolate* isolate, Handle<Object> value) { 786 i::Handle<i::Symbol> symbol(isolate->context()->wasm_table_sym(), isolate); 787 return HasBrand(value, symbol); 788 } 789 } // namespace internal 790 } // namespace v8 791