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.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