wasm-js.cc 30 KB
Newer Older
1 2 3 4 5
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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 11
#include "src/assert-scope.h"
#include "src/ast/ast.h"
12
#include "src/execution.h"
13 14 15 16
#include "src/factory.h"
#include "src/handles.h"
#include "src/isolate.h"
#include "src/objects.h"
17
#include "src/parsing/parse-info.h"
18 19 20

#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-js.h"
21
#include "src/wasm/wasm-limits.h"
22
#include "src/wasm/wasm-module.h"
23
#include "src/wasm/wasm-objects.h"
24 25 26 27 28 29 30 31 32
#include "src/wasm/wasm-result.h"

typedef uint8_t byte;

using v8::internal::wasm::ErrorThrower;

namespace v8 {

namespace {
33 34 35 36 37 38 39
i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
  return isolate->factory()->NewStringFromAsciiChecked(str);
}
Local<String> v8_str(Isolate* isolate, const char* str) {
  return Utils::ToLocal(v8_str(reinterpret_cast<i::Isolate*>(isolate), str));
}

40 41 42 43 44 45
struct RawBuffer {
  const byte* start;
  const byte* end;
  size_t size() { return static_cast<size_t>(end - start); }
};

rossberg's avatar
rossberg committed
46 47
RawBuffer GetRawBufferSource(
    v8::Local<v8::Value> source, ErrorThrower* thrower) {
48 49
  const byte* start = nullptr;
  const byte* end = nullptr;
50

rossberg's avatar
rossberg committed
51
  if (source->IsArrayBuffer()) {
52
    // A raw array buffer was passed.
rossberg's avatar
rossberg committed
53
    Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source);
54 55 56 57 58 59
    ArrayBuffer::Contents contents = buffer->GetContents();

    start = reinterpret_cast<const byte*>(contents.Data());
    end = start + contents.ByteLength();

    if (start == nullptr || end == start) {
60
      thrower->CompileError("ArrayBuffer argument is empty");
61
    }
rossberg's avatar
rossberg committed
62
  } else if (source->IsTypedArray()) {
63
    // A TypedArray was passed.
rossberg's avatar
rossberg committed
64
    Local<TypedArray> array = Local<TypedArray>::Cast(source);
65 66 67 68 69 70 71 72 73
    Local<ArrayBuffer> buffer = array->Buffer();

    ArrayBuffer::Contents contents = buffer->GetContents();

    start =
        reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset();
    end = start + array->ByteLength();

    if (start == nullptr || end == start) {
74
      thrower->TypeError("ArrayBuffer argument is empty");
75 76
    }
  } else {
77
    thrower->TypeError("Argument 0 must be an ArrayBuffer or Uint8Array");
78
  }
79

80 81 82
  return {start, end};
}

83
static i::MaybeHandle<i::WasmModuleObject> CreateModuleObject(
rossberg's avatar
rossberg committed
84 85 86
    v8::Isolate* isolate, const v8::Local<v8::Value> source,
    ErrorThrower* thrower) {
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
87
  i::MaybeHandle<i::JSObject> nothing;
rossberg's avatar
rossberg committed
88 89

  RawBuffer buffer = GetRawBufferSource(source, thrower);
90
  if (buffer.start == nullptr) return i::MaybeHandle<i::WasmModuleObject>();
rossberg's avatar
rossberg committed
91 92

  DCHECK(source->IsArrayBuffer() || source->IsTypedArray());
93
  return i::wasm::CreateModuleObjectFromBytes(
94
      i_isolate, buffer.start, buffer.end, thrower, i::wasm::kWasmOrigin,
95
      i::Handle<i::Script>::null(), i::Vector<const byte>::empty());
rossberg's avatar
rossberg committed
96 97
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
static bool ValidateModule(v8::Isolate* isolate,
                           const v8::Local<v8::Value> source,
                           ErrorThrower* thrower) {
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  i::MaybeHandle<i::JSObject> nothing;

  RawBuffer buffer = GetRawBufferSource(source, thrower);
  if (buffer.start == nullptr) return false;

  DCHECK(source->IsArrayBuffer() || source->IsTypedArray());
  return i::wasm::ValidateModuleBytes(i_isolate, buffer.start, buffer.end,
                                      thrower,
                                      i::wasm::ModuleOrigin::kWasmOrigin);
}

113 114
static bool BrandCheck(Isolate* isolate, i::Handle<i::Object> value,
                       i::Handle<i::Symbol> sym, const char* msg) {
115 116 117 118 119 120 121 122 123 124 125
  if (value->IsJSObject()) {
    i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value);
    Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym);
    if (has_brand.IsNothing()) return false;
    if (has_brand.ToChecked()) return true;
  }
  v8::Local<v8::Value> e = v8::Exception::TypeError(v8_str(isolate, msg));
  isolate->ThrowException(e);
  return false;
}

rossberg's avatar
rossberg committed
126 127 128 129 130 131 132
void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
                       "WebAssembly.compile()");

  if (args.Length() < 1) {
133
    thrower.TypeError("Argument 0 must be a buffer source");
rossberg's avatar
rossberg committed
134 135 136 137 138 139 140 141
    return;
  }
  i::MaybeHandle<i::JSObject> module_obj =
      CreateModuleObject(isolate, args[0], &thrower);

  Local<Context> context = isolate->GetCurrentContext();
  v8::Local<v8::Promise::Resolver> resolver;
  if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return;
142 143 144 145 146
  if (thrower.error()) {
    resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
  } else {
    resolver->Resolve(context, Utils::ToLocal(module_obj.ToHandleChecked()));
  }
rossberg's avatar
rossberg committed
147 148 149 150
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(resolver->GetPromise());
}

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
                       "WebAssembly.validate()");

  if (args.Length() < 1) {
    thrower.TypeError("Argument 0 must be a buffer source");
    return;
  }

  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  if (ValidateModule(isolate, args[0], &thrower)) {
    return_value.Set(v8::True(isolate));
  } else {
    return_value.Set(v8::False(isolate));
  }
}

rossberg's avatar
rossberg committed
170 171 172 173 174 175 176
void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
                       "WebAssembly.Module()");

  if (args.Length() < 1) {
177
    thrower.TypeError("Argument 0 must be a buffer source");
rossberg's avatar
rossberg committed
178 179 180 181 182 183 184 185 186 187 188 189 190
    return;
  }
  i::MaybeHandle<i::JSObject> module_obj =
      CreateModuleObject(isolate, args[0], &thrower);
  if (module_obj.is_null()) return;

  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(Utils::ToLocal(module_obj.ToHandleChecked()));
}

void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
  HandleScope scope(args.GetIsolate());
  v8::Isolate* isolate = args.GetIsolate();
191 192 193
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);

  ErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
194

rossberg's avatar
rossberg committed
195
  if (args.Length() < 1) {
196
    thrower.TypeError("Argument 0 must be a WebAssembly.Module");
rossberg's avatar
rossberg committed
197 198
    return;
  }
199

rossberg's avatar
rossberg committed
200 201
  Local<Context> context = isolate->GetCurrentContext();
  i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
202 203 204
  if (!BrandCheck(isolate, Utils::OpenHandle(*args[0]),
                  i::Handle<i::Symbol>(i_context->wasm_module_sym()),
                  "Argument 0 must be a WebAssembly.Module")) {
205 206
    return;
  }
rossberg's avatar
rossberg committed
207

208
  Local<Object> obj = Local<Object>::Cast(args[0]);
209
  i::Handle<i::JSObject> i_obj =
210 211 212 213 214 215 216 217 218
      i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj));

  i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null();
  if (args.Length() > 1 && args[1]->IsObject()) {
    Local<Object> obj = Local<Object>::Cast(args[1]);
    ffi = i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
  }

  i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
219
  if (args.Length() > 2 && args[2]->IsObject()) {
220 221
    Local<Object> obj = Local<Object>::Cast(args[2]);
    i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
222
    if (i::WasmJs::IsWasmMemoryObject(i_isolate, mem_obj)) {
223
      memory = i::Handle<i::JSArrayBuffer>(
224
          i::Handle<i::WasmMemoryObject>::cast(mem_obj)->buffer(), i_isolate);
225 226
    } else {
      thrower.TypeError("Argument 2 must be a WebAssembly.Memory");
227
      return;
228
    }
229
  }
230 231
  i::MaybeHandle<i::JSObject> instance =
      i::wasm::WasmModule::Instantiate(i_isolate, &thrower, i_obj, ffi, memory);
232
  if (instance.is_null()) {
233
    if (!thrower.error()) thrower.RuntimeError("Could not instantiate module");
234 235
    return;
  }
236
  DCHECK(!i_isolate->has_pending_exception());
237 238
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(Utils::ToLocal(instance.ToHandleChecked()));
239
}
240 241 242 243 244 245 246

bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
                        Local<Context> context, Local<v8::Object> object,
                        Local<String> property, int* result, int lower_bound,
                        int upper_bound) {
  v8::MaybeLocal<v8::Value> maybe = object->Get(context, property);
  v8::Local<v8::Value> value;
247
  if (maybe.ToLocal(&value)) {
248 249 250 251 252 253 254 255
    int64_t number;
    if (!value->IntegerValue(context).To(&number)) return false;
    if (number < static_cast<int64_t>(lower_bound)) {
      thrower->RangeError("Property value %" PRId64
                          " is below the lower bound %d",
                          number, lower_bound);
      return false;
    }
256
    if (number > static_cast<int64_t>(upper_bound)) {
257 258 259 260 261
      thrower->RangeError("Property value %" PRId64
                          " is above the upper bound %d",
                          number, upper_bound);
      return false;
    }
262
    *result = static_cast<int>(number);
263 264 265 266 267
    return true;
  }
  return false;
}

268 269
const int max_table_size = 1 << 26;

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
                       "WebAssembly.Module()");
  if (args.Length() < 1 || !args[0]->IsObject()) {
    thrower.TypeError("Argument 0 must be a table descriptor");
    return;
  }
  Local<Context> context = isolate->GetCurrentContext();
  Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked();
  // The descriptor's 'element'.
  {
    v8::MaybeLocal<v8::Value> maybe =
        descriptor->Get(context, v8_str(isolate, "element"));
    v8::Local<v8::Value> value;
    if (!maybe.ToLocal(&value)) return;
    v8::Local<v8::String> string;
    if (!value->ToString(context).ToLocal(&string)) return;
    bool equal;
    if (!string->Equals(context, v8_str(isolate, "anyfunc")).To(&equal)) return;
    if (!equal) {
      thrower.TypeError("Descriptor property 'element' must be 'anyfunc'");
      return;
    }
  }
  // The descriptor's 'initial'.
  int initial;
  if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
                          v8_str(isolate, "initial"), &initial, 0,
300
                          max_table_size)) {
301 302 303
    return;
  }
  // The descriptor's 'maximum'.
jyan's avatar
jyan committed
304
  int maximum = 0;
305 306 307 308 309 310 311 312 313 314
  Local<String> maximum_key = v8_str(isolate, "maximum");
  Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);

  if (has_maximum.IsNothing()) {
    // There has been an exception, just return.
    return;
  }
  if (has_maximum.FromJust()) {
    if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
                            &maximum, initial, max_table_size)) {
315 316
      return;
    }
317
  } else {
318
    maximum = static_cast<int>(i::wasm::kV8MaxWasmTableSize);
319 320 321
  }

  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
322
  i::Handle<i::FixedArray> fixed_array;
323 324
  i::Handle<i::JSObject> table_obj =
      i::WasmTableObject::New(i_isolate, initial, maximum, &fixed_array);
325 326 327
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(Utils::ToLocal(table_obj));
}
328

329 330 331 332
void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
333
                       "WebAssembly.Memory()");
334
  if (args.Length() < 1 || !args[0]->IsObject()) {
335
    thrower.TypeError("Argument 0 must be a memory descriptor");
336 337 338 339 340 341
    return;
  }
  Local<Context> context = isolate->GetCurrentContext();
  Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked();
  // The descriptor's 'initial'.
  int initial;
342 343 344 345
  if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
                          v8_str(isolate, "initial"), &initial, 0, 65536)) {
    return;
  }
346
  // The descriptor's 'maximum'.
jyan's avatar
jyan committed
347
  int maximum = 0;
348 349 350 351 352 353 354 355 356 357
  Local<String> maximum_key = v8_str(isolate, "maximum");
  Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);

  if (has_maximum.IsNothing()) {
    // There has been an exception, just return.
    return;
  }
  if (has_maximum.FromJust()) {
    if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
                            &maximum, initial, 65536)) {
358 359 360 361 362 363
      return;
    }
  }
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) *
                static_cast<size_t>(initial);
364 365
  i::Handle<i::JSArrayBuffer> buffer =
      i::wasm::NewArrayBuffer(i_isolate, size, i::FLAG_wasm_guard_pages);
366 367 368 369
  if (buffer.is_null()) {
    thrower.RangeError("could not allocate memory");
    return;
  }
370 371 372
  i::Handle<i::JSObject> memory_obj = i::WasmMemoryObject::New(
      i_isolate, buffer, has_maximum.FromJust() ? maximum : -1);
  args.GetReturnValue().Set(Utils::ToLocal(memory_obj));
373
}
374

375 376
void WebAssemblyTableGetLength(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
377 378 379 380 381 382 383 384
  v8::Isolate* isolate = args.GetIsolate();
  Local<Context> context = isolate->GetCurrentContext();
  i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
  if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()),
                  i::Handle<i::Symbol>(i_context->wasm_table_sym()),
                  "Receiver is not a WebAssembly.Table")) {
    return;
  }
385 386 387 388
  auto receiver =
      i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
  args.GetReturnValue().Set(
      v8::Number::New(isolate, receiver->current_length()));
389
}
390

391
void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
392 393 394 395 396 397 398 399
  v8::Isolate* isolate = args.GetIsolate();
  Local<Context> context = isolate->GetCurrentContext();
  i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
  if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()),
                  i::Handle<i::Symbol>(i_context->wasm_table_sym()),
                  "Receiver is not a WebAssembly.Table")) {
    return;
  }
400 401

  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
402 403
  auto receiver =
      i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
404
  i::Handle<i::FixedArray> old_array(receiver->functions(), i_isolate);
405 406 407 408 409 410 411
  int old_size = old_array->length();
  int64_t new_size64 = 0;
  if (args.Length() > 0 && !args[0]->IntegerValue(context).To(&new_size64)) {
    return;
  }
  new_size64 += old_size;

412
  if (new_size64 < old_size || new_size64 > receiver->maximum_length()) {
413 414 415 416 417 418 419 420 421 422 423 424 425 426
    v8::Local<v8::Value> e = v8::Exception::RangeError(
        v8_str(isolate, new_size64 < old_size ? "trying to shrink table"
                                              : "maximum table size exceeded"));
    isolate->ThrowException(e);
    return;
  }
  int new_size = static_cast<int>(new_size64);

  if (new_size != old_size) {
    i::Handle<i::FixedArray> new_array =
        i_isolate->factory()->NewFixedArray(new_size);
    for (int i = 0; i < old_size; ++i) new_array->set(i, old_array->get(i));
    i::Object* null = i_isolate->heap()->null_value();
    for (int i = old_size; i < new_size; ++i) new_array->set(i, null);
427
    receiver->set_functions(*new_array);
428 429 430
  }

  // TODO(titzer): update relevant instances.
431
}
432

433
void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
434 435 436 437 438 439 440 441 442 443
  v8::Isolate* isolate = args.GetIsolate();
  Local<Context> context = isolate->GetCurrentContext();
  i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
  if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()),
                  i::Handle<i::Symbol>(i_context->wasm_table_sym()),
                  "Receiver is not a WebAssembly.Table")) {
    return;
  }

  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
444 445
  auto receiver =
      i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
446
  i::Handle<i::FixedArray> array(receiver->functions(), i_isolate);
447 448 449
  int i = 0;
  if (args.Length() > 0 && !args[0]->Int32Value(context).To(&i)) return;
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
450
  if (i < 0 || i >= array->length()) {
451 452 453 454
    v8::Local<v8::Value> e =
        v8::Exception::RangeError(v8_str(isolate, "index out of bounds"));
    isolate->ThrowException(e);
    return;
455
  }
456

457
  i::Handle<i::Object> value(array->get(i), i_isolate);
458
  return_value.Set(Utils::ToLocal(value));
459
}
460

461
void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
462
  v8::Isolate* isolate = args.GetIsolate();
463
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
464 465 466 467 468 469 470
  Local<Context> context = isolate->GetCurrentContext();
  i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
  if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()),
                  i::Handle<i::Symbol>(i_context->wasm_table_sym()),
                  "Receiver is not a WebAssembly.Table")) {
    return;
  }
471
  if (args.Length() < 2) {
472 473 474 475 476
    v8::Local<v8::Value> e = v8::Exception::TypeError(
        v8_str(isolate, "Argument 1 must be null or a function"));
    isolate->ThrowException(e);
    return;
  }
477 478 479 480 481 482 483 484 485 486
  i::Handle<i::Object> value = Utils::OpenHandle(*args[1]);
  if (!value->IsNull(i_isolate) &&
      (!value->IsJSFunction() ||
       i::Handle<i::JSFunction>::cast(value)->code()->kind() !=
           i::Code::JS_TO_WASM_FUNCTION)) {
    v8::Local<v8::Value> e = v8::Exception::TypeError(
        v8_str(isolate, "Argument 1 must be null or a WebAssembly function"));
    isolate->ThrowException(e);
    return;
  }
487

488 489
  auto receiver =
      i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
490
  i::Handle<i::FixedArray> array(receiver->functions(), i_isolate);
491 492
  int i;
  if (!args[0]->Int32Value(context).To(&i)) return;
493
  if (i < 0 || i >= array->length()) {
494 495 496 497 498 499
    v8::Local<v8::Value> e =
        v8::Exception::RangeError(v8_str(isolate, "index out of bounds"));
    isolate->ThrowException(e);
    return;
  }

500
  i::Handle<i::FixedArray> dispatch_tables(receiver->dispatch_tables(),
501
                                           i_isolate);
502 503 504 505 506 507 508
  if (value->IsNull(i_isolate)) {
    i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i,
                                  i::Handle<i::JSFunction>::null());
  } else {
    i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i,
                                  i::Handle<i::JSFunction>::cast(value));
  }
509

510
  i::Handle<i::FixedArray>::cast(array)->set(i, *value);
511
}
512

513
void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
514 515 516 517 518 519 520 521
  v8::Isolate* isolate = args.GetIsolate();
  Local<Context> context = isolate->GetCurrentContext();
  i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
  if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()),
                  i::Handle<i::Symbol>(i_context->wasm_memory_sym()),
                  "Receiver is not a WebAssembly.Memory")) {
    return;
  }
522 523 524 525 526 527
  if (args.Length() < 1) {
    v8::Local<v8::Value> e = v8::Exception::TypeError(
        v8_str(isolate, "Argument 0 required, must be numeric value of pages"));
    isolate->ThrowException(e);
    return;
  }
528

529 530
  uint32_t delta = args[0]->Uint32Value(context).FromJust();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
531 532 533
  i::Handle<i::Object> receiver =
      i::Handle<i::Object>::cast(Utils::OpenHandle(*args.This()));
  int32_t ret = i::wasm::GrowWebAssemblyMemory(i_isolate, receiver, delta);
534 535 536 537 538 539 540 541
  if (ret == -1) {
    v8::Local<v8::Value> e = v8::Exception::Error(
        v8_str(isolate, "Unable to grow instance memory."));
    isolate->ThrowException(e);
    return;
  }
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(ret);
542
}
543

544 545 546 547 548 549 550 551 552 553 554
void WebAssemblyMemoryGetBuffer(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  Local<Context> context = isolate->GetCurrentContext();
  i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
  if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()),
                  i::Handle<i::Symbol>(i_context->wasm_memory_sym()),
                  "Receiver is not a WebAssembly.Memory")) {
    return;
  }
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
555 556
  i::Handle<i::WasmMemoryObject> receiver =
      i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This()));
557
  i::Handle<i::Object> buffer(receiver->buffer(), i_isolate);
558 559 560 561
  DCHECK(buffer->IsJSArrayBuffer());
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(Utils::ToLocal(buffer));
}
562 563 564 565 566 567 568 569 570 571 572 573 574
}  // namespace

// TODO(titzer): we use the API to create the function template because the
// internal guts are too ugly to replicate here.
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
                                                      FunctionCallback func) {
  Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
  Local<FunctionTemplate> local = FunctionTemplate::New(isolate, func);
  return v8::Utils::OpenHandle(*local);
}

namespace internal {

575
Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object,
576 577
                               const char* str, FunctionCallback func,
                               int length = 0) {
578 579 580 581
  Handle<String> name = v8_str(isolate, str);
  Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func);
  Handle<JSFunction> function =
      ApiNatives::InstantiateFunction(temp).ToHandleChecked();
582 583
  JSFunction::SetName(function, name, isolate->factory()->empty_string());
  function->shared()->set_length(length);
584
  PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
585
  JSObject::AddProperty(object, name, function, attributes);
rossberg's avatar
rossberg committed
586
  return function;
587 588
}

589 590 591 592 593 594 595
Handle<JSFunction> InstallGetter(Isolate* isolate, Handle<JSObject> object,
                                 const char* str, FunctionCallback func) {
  Handle<String> name = v8_str(isolate, str);
  Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func);
  Handle<JSFunction> function =
      ApiNatives::InstantiateFunction(temp).ToHandleChecked();
  v8::PropertyAttribute attributes =
596
      static_cast<v8::PropertyAttribute>(v8::DontEnum);
597 598 599 600 601 602
  Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name),
                                              Utils::ToLocal(function),
                                              Local<Function>(), attributes);
  return function;
}

603 604 605 606 607
void WasmJs::InstallWasmModuleSymbolIfNeeded(Isolate* isolate,
                                             Handle<JSGlobalObject> global,
                                             Handle<Context> context) {
  if (!context->get(Context::WASM_MODULE_SYM_INDEX)->IsSymbol() ||
      !context->get(Context::WASM_INSTANCE_SYM_INDEX)->IsSymbol()) {
608 609 610
    InstallWasmMapsIfNeeded(isolate, isolate->native_context());
    InstallWasmConstructors(isolate, isolate->global_object(),
                            isolate->native_context());
611
  }
612 613
}

614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
void WasmJs::InstallWasmConstructors(Isolate* isolate,
                                     Handle<JSGlobalObject> global,
                                     Handle<Context> context) {
  Factory* factory = isolate->factory();
  // Create private symbols.
  Handle<Symbol> module_sym = factory->NewPrivateSymbol();
  context->set_wasm_module_sym(*module_sym);

  Handle<Symbol> instance_sym = factory->NewPrivateSymbol();
  context->set_wasm_instance_sym(*instance_sym);

  Handle<Symbol> table_sym = factory->NewPrivateSymbol();
  context->set_wasm_table_sym(*table_sym);

  Handle<Symbol> memory_sym = factory->NewPrivateSymbol();
  context->set_wasm_memory_sym(*memory_sym);

  // Bind the WebAssembly object.
  Handle<String> name = v8_str(isolate, "WebAssembly");
  Handle<JSFunction> cons = factory->NewFunction(name);
  JSFunction::SetInstancePrototype(
      cons, Handle<Object>(context->initial_object_prototype(), isolate));
  cons->shared()->set_instance_class_name(*name);
637
  Handle<JSObject> webassembly = factory->NewJSObject(cons, TENURED);
638
  PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
639
  JSObject::AddProperty(global, name, webassembly, attributes);
640 641

  // Setup compile
642
  InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile, 1);
643

644
  // Setup compile
645
  InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate, 1);
646

647 648
  // Setup Module
  Handle<JSFunction> module_constructor =
649
      InstallFunc(isolate, webassembly, "Module", WebAssemblyModule, 1);
650 651 652 653
  context->set_wasm_module_constructor(*module_constructor);
  Handle<JSObject> module_proto =
      factory->NewJSObject(module_constructor, TENURED);
  i::Handle<i::Map> map = isolate->factory()->NewMap(
654 655
      i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize +
                             WasmModuleObject::kFieldCount * i::kPointerSize);
656 657 658 659 660 661
  JSFunction::SetInitialMap(module_constructor, map, module_proto);
  JSObject::AddProperty(module_proto, isolate->factory()->constructor_string(),
                        module_constructor, DONT_ENUM);

  // Setup Instance
  Handle<JSFunction> instance_constructor =
662
      InstallFunc(isolate, webassembly, "Instance", WebAssemblyInstance, 1);
663 664 665 666
  context->set_wasm_instance_constructor(*instance_constructor);

  // Setup Table
  Handle<JSFunction> table_constructor =
667
      InstallFunc(isolate, webassembly, "Table", WebAssemblyTable, 1);
668 669 670 671
  context->set_wasm_table_constructor(*table_constructor);
  Handle<JSObject> table_proto =
      factory->NewJSObject(table_constructor, TENURED);
  map = isolate->factory()->NewMap(
672
      i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize +
673
                             WasmTableObject::kFieldCount * i::kPointerSize);
674 675 676 677
  JSFunction::SetInitialMap(table_constructor, map, table_proto);
  JSObject::AddProperty(table_proto, isolate->factory()->constructor_string(),
                        table_constructor, DONT_ENUM);
  InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength);
678 679 680
  InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow, 1);
  InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet, 1);
  InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet, 2);
681 682 683

  // Setup Memory
  Handle<JSFunction> memory_constructor =
684
      InstallFunc(isolate, webassembly, "Memory", WebAssemblyMemory, 1);
685 686 687 688
  context->set_wasm_memory_constructor(*memory_constructor);
  Handle<JSObject> memory_proto =
      factory->NewJSObject(memory_constructor, TENURED);
  map = isolate->factory()->NewMap(
689
      i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize +
690
                             WasmMemoryObject::kFieldCount * i::kPointerSize);
691 692 693
  JSFunction::SetInitialMap(memory_constructor, map, memory_proto);
  JSObject::AddProperty(memory_proto, isolate->factory()->constructor_string(),
                        memory_constructor, DONT_ENUM);
694
  InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow, 1);
695
  InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer);
696 697

  // Setup errors
698
  attributes = static_cast<PropertyAttributes>(DONT_ENUM);
699 700
  Handle<JSFunction> compile_error(
      isolate->native_context()->wasm_compile_error_function());
701
  JSObject::AddProperty(webassembly, isolate->factory()->CompileError_string(),
702
                        compile_error, attributes);
703 704 705 706
  Handle<JSFunction> link_error(
      isolate->native_context()->wasm_link_error_function());
  JSObject::AddProperty(webassembly, isolate->factory()->LinkError_string(),
                        link_error, attributes);
707 708
  Handle<JSFunction> runtime_error(
      isolate->native_context()->wasm_runtime_error_function());
709
  JSObject::AddProperty(webassembly, isolate->factory()->RuntimeError_string(),
710
                        runtime_error, attributes);
711 712
}

713
void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
titzer's avatar
titzer committed
714 715 716 717
  if (!FLAG_expose_wasm && !FLAG_validate_asm) {
    return;
  }

718
  // Setup wasm function map.
719
  Handle<Context> context(global->native_context(), isolate);
720
  InstallWasmMapsIfNeeded(isolate, context);
721

722 723
  if (FLAG_expose_wasm) {
    InstallWasmConstructors(isolate, global, context);
rossberg's avatar
rossberg committed
724
  }
725
}
726

727 728
void WasmJs::InstallWasmMapsIfNeeded(Isolate* isolate,
                                     Handle<Context> context) {
729
  if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) {
730 731 732 733 734 735 736 737 738
    // TODO(titzer): Move this to bootstrapper.cc??
    // TODO(titzer): Also make one for strict mode functions?
    Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate);

    InstanceType instance_type = prev_map->instance_type();
    int internal_fields = JSObject::GetInternalFieldCount(*prev_map);
    CHECK_EQ(0, internal_fields);
    int pre_allocated =
        prev_map->GetInObjectProperties() - prev_map->unused_property_fields();
739 740 741 742 743 744
    int instance_size = 0;
    int in_object_properties = 0;
    int wasm_internal_fields = internal_fields + 1  // module instance object
                               + 1                  // function arity
                               + 1;                 // function signature
    JSFunction::CalculateInstanceSizeHelper(instance_type, wasm_internal_fields,
745 746 747 748 749 750 751 752
                                            0, &instance_size,
                                            &in_object_properties);

    int unused_property_fields = in_object_properties - pre_allocated;
    Handle<Map> map = Map::CopyInitialMap(
        prev_map, instance_size, in_object_properties, unused_property_fields);

    context->set_wasm_function_map(*map);
753 754 755
  }
}

756
static bool HasBrand(i::Handle<i::Object> value, i::Handle<i::Symbol> symbol) {
757 758
  if (value->IsJSObject()) {
    i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value);
759
    Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, symbol);
760 761 762 763 764 765
    if (has_brand.IsNothing()) return false;
    if (has_brand.ToChecked()) return true;
  }
  return false;
}

766 767 768 769 770 771 772 773 774
bool WasmJs::IsWasmMemoryObject(Isolate* isolate, Handle<Object> value) {
  i::Handle<i::Symbol> symbol(isolate->context()->wasm_memory_sym(), isolate);
  return HasBrand(value, symbol);
}

bool WasmJs::IsWasmTableObject(Isolate* isolate, Handle<Object> value) {
  i::Handle<i::Symbol> symbol(isolate->context()->wasm_table_sym(), isolate);
  return HasBrand(value, symbol);
}
775 776
}  // namespace internal
}  // namespace v8