Home | History | Annotate | Download | only in wasm
      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