Commit c44228b7 authored by Deepti Gandluri's avatar Deepti Gandluri Committed by Commit Bot

[wasm] WebAssembly.Memory constructor should accept SharedArrayBuffers.

 - Implement js-api changes for WebAssembly.Memory to accept a shared parameter
 - Update allocation to use SharedArrayBuffers

BUG=v8:6532

R=binji@chromium.org, bradnelson@chromium.org

Change-Id: I021491217568751b06fbd7b4b08b1dd88910e21d
Reviewed-on: https://chromium-review.googlesource.com/564058
Commit-Queue: Deepti Gandluri <gdeepti@chromium.org>
Reviewed-by: 's avatarBrad Nelson <bradnelson@chromium.org>
Reviewed-by: 's avatarBen Smith <binji@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46543}
parent 61ea3243
......@@ -564,8 +564,8 @@ DEFINE_BOOL(experimental_wasm_eh, false,
"enable prototype exception handling opcodes for wasm")
DEFINE_BOOL(experimental_wasm_mv, false,
"enable prototype multi-value support for wasm")
DEFINE_BOOL(experimental_wasm_atomics, false,
"enable prototype atomic opcodes for wasm")
DEFINE_BOOL(experimental_wasm_threads, false,
"enable prototype threads for wasm")
DEFINE_BOOL(wasm_opt, false, "enable wasm optimization")
DEFINE_BOOL(wasm_no_bounds_checks, false,
......
......@@ -1326,7 +1326,7 @@ class WasmFullDecoder : public WasmDecoder {
error("Atomics are allowed only in AsmJs modules");
break;
}
CHECK_PROTOTYPE_OPCODE(atomics);
CHECK_PROTOTYPE_OPCODE(threads);
len = 2;
byte atomic_opcode = read_u8<true>(pc_ + 1, "atomic index");
opcode = static_cast<WasmOpcode>(opcode << 8 | atomic_opcode);
......
......@@ -581,14 +581,43 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
return;
}
}
bool is_shared_memory = false;
if (i::FLAG_experimental_wasm_threads) {
// Shared property of descriptor
Local<String> shared_key = v8_str(isolate, "shared");
Maybe<bool> has_shared = descriptor->Has(context, shared_key);
if (!has_shared.IsNothing() && has_shared.FromJust()) {
v8::MaybeLocal<v8::Value> maybe = descriptor->Get(context, shared_key);
v8::Local<v8::Value> value;
if (maybe.ToLocal(&value)) {
if (!value->BooleanValue(context).To(&is_shared_memory)) return;
}
}
// Throw TypeError if shared is true, and the descriptor has no "maximum"
if (is_shared_memory && maximum == -1) {
thrower.TypeError(
"If shared is true, maximum property should be defined.");
}
}
size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) *
static_cast<size_t>(initial);
i::Handle<i::JSArrayBuffer> buffer =
i::wasm::NewArrayBuffer(i_isolate, size, i::FLAG_wasm_guard_pages);
i::Handle<i::JSArrayBuffer> buffer = i::wasm::NewArrayBuffer(
i_isolate, size, i::FLAG_wasm_guard_pages,
is_shared_memory ? i::SharedFlag::kShared : i::SharedFlag::kNotShared);
if (buffer.is_null()) {
thrower.RangeError("could not allocate memory");
return;
}
if (buffer->is_shared()) {
Maybe<bool> result =
buffer->SetIntegrityLevel(buffer, i::FROZEN, i::Object::DONT_THROW);
if (!result.FromJust()) {
thrower.TypeError(
"Status of setting SetIntegrityLevel of buffer is false.");
}
}
i::Handle<i::JSObject> memory_obj = i::WasmMemoryObject::New(
i_isolate, buffer, static_cast<int32_t>(maximum));
args.GetReturnValue().Set(Utils::ToLocal(memory_obj));
......@@ -756,7 +785,9 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
return;
}
bool free_memory = (delta_size != 0);
i::wasm::DetachWebAssemblyMemoryBuffer(i_isolate, old_buffer, free_memory);
if (!old_buffer->is_shared()) {
i::wasm::DetachWebAssemblyMemoryBuffer(i_isolate, old_buffer, free_memory);
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(ret);
}
......@@ -770,8 +801,20 @@ void WebAssemblyMemoryGetBuffer(
ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.buffer");
EXTRACT_THIS(receiver, WasmMemoryObject);
i::Handle<i::Object> buffer(receiver->array_buffer(), i_isolate);
DCHECK(buffer->IsJSArrayBuffer());
i::Handle<i::Object> buffer_obj(receiver->array_buffer(), i_isolate);
DCHECK(buffer_obj->IsJSArrayBuffer());
i::Handle<i::JSArrayBuffer> buffer(i::JSArrayBuffer::cast(*buffer_obj));
if (buffer->is_shared()) {
// TODO(gdeepti): More needed here for when cached buffer, and current
// buffer are out of sync, handle that here when bounds checks, and Grow
// are handled correctly.
Maybe<bool> result =
buffer->SetIntegrityLevel(buffer, i::FROZEN, i::Object::DONT_THROW);
if (!result.FromJust()) {
thrower.TypeError(
"Status of setting SetIntegrityLevel of buffer is false.");
}
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(buffer));
}
......
......@@ -216,17 +216,16 @@ void RecordLazyCodeStats(Code* code, Counters* counters) {
// static
const WasmExceptionSig wasm::WasmException::empty_sig_(0, 0, nullptr);
Handle<JSArrayBuffer> wasm::SetupArrayBuffer(Isolate* isolate,
void* allocation_base,
size_t allocation_length,
void* backing_store, size_t size,
bool is_external,
bool enable_guard_regions) {
Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
Handle<JSArrayBuffer> wasm::SetupArrayBuffer(
Isolate* isolate, void* allocation_base, size_t allocation_length,
void* backing_store, size_t size, bool is_external,
bool enable_guard_regions, SharedFlag shared) {
Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(shared);
DCHECK_GE(kMaxInt, size);
if (shared == SharedFlag::kShared) DCHECK(FLAG_experimental_wasm_threads);
JSArrayBuffer::Setup(buffer, isolate, is_external, allocation_base,
allocation_length, backing_store,
static_cast<int>(size));
allocation_length, backing_store, static_cast<int>(size),
shared);
buffer->set_is_neuterable(false);
buffer->set_is_wasm_buffer(true);
buffer->set_has_guard_region(enable_guard_regions);
......@@ -234,7 +233,8 @@ Handle<JSArrayBuffer> wasm::SetupArrayBuffer(Isolate* isolate,
}
Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size,
bool enable_guard_regions) {
bool enable_guard_regions,
SharedFlag shared) {
// Check against kMaxInt, since the byte length is stored as int in the
// JSArrayBuffer. Note that wasm_max_mem_pages can be raised from the command
// line, and we don't want to fail a CHECK then.
......@@ -265,7 +265,7 @@ Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size,
constexpr bool is_external = false;
return SetupArrayBuffer(isolate, allocation_base, allocation_length, memory,
size, is_external, enable_guard_regions);
size, is_external, enable_guard_regions, shared);
}
void wasm::UnpackAndRegisterProtectedInstructions(
......
......@@ -430,14 +430,14 @@ Handle<FixedArray> DecodeLocalNames(Isolate*, Handle<WasmCompiledModule>);
// Returns nullptr on failing to get owning instance.
WasmInstanceObject* GetOwningWasmInstance(Code* code);
Handle<JSArrayBuffer> NewArrayBuffer(Isolate*, size_t size,
bool enable_guard_regions);
Handle<JSArrayBuffer> NewArrayBuffer(
Isolate*, size_t size, bool enable_guard_regions,
SharedFlag shared = SharedFlag::kNotShared);
Handle<JSArrayBuffer> SetupArrayBuffer(Isolate*, void* allocation_base,
size_t allocation_length,
void* backing_store, size_t size,
bool is_external,
bool enable_guard_regions);
Handle<JSArrayBuffer> SetupArrayBuffer(
Isolate*, void* allocation_base, size_t allocation_length,
void* backing_store, size_t size, bool is_external,
bool enable_guard_regions, SharedFlag shared = SharedFlag::kNotShared);
void DetachWebAssemblyMemoryBuffer(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
......@@ -494,7 +494,16 @@ const bool kGuardRegionsSupported = false;
#endif
inline bool EnableGuardRegions() {
return FLAG_wasm_guard_pages && kGuardRegionsSupported;
return FLAG_wasm_guard_pages && kGuardRegionsSupported &&
!FLAG_experimental_wasm_threads;
}
inline SharedFlag IsShared(Handle<JSArrayBuffer> buffer) {
if (!buffer.is_null() && buffer->is_shared()) {
DCHECK(FLAG_experimental_wasm_threads);
return SharedFlag::kShared;
}
return SharedFlag::kNotShared;
}
void UnpackAndRegisterProtectedInstructions(Isolate* isolate,
......
......@@ -274,8 +274,8 @@ Handle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate,
: old_buffer->has_guard_region();
size_t new_size =
static_cast<size_t>(old_pages + pages) * WasmModule::kPageSize;
Handle<JSArrayBuffer> new_buffer =
NewArrayBuffer(isolate, new_size, enable_guard_regions);
Handle<JSArrayBuffer> new_buffer = NewArrayBuffer(
isolate, new_size, enable_guard_regions, IsShared(old_buffer));
if (new_buffer.is_null()) return new_buffer;
Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
memcpy(new_mem_start, old_mem_start, old_size);
......@@ -369,7 +369,8 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate,
new_buffer = SetupArrayBuffer(
isolate, old_buffer->allocation_base(),
old_buffer->allocation_length(), old_buffer->backing_store(),
old_size, old_buffer->is_external(), old_buffer->has_guard_region());
old_size, old_buffer->is_external(), old_buffer->has_guard_region(),
IsShared(old_buffer));
memory_object->set_array_buffer(*new_buffer);
}
DCHECK_EQ(0, old_size % WasmModule::kPageSize);
......
// Copyright 2017 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.
// Flags: --experimental-wasm-threads
function assertMemoryIsValid(memory) {
assertSame(WebAssembly.Memory.prototype, memory.__proto__);
assertSame(WebAssembly.Memory, memory.constructor);
assertTrue(memory instanceof Object);
assertTrue(memory instanceof WebAssembly.Memory);
}
(function TestConstructorWithShared() {
print("TestConstructorWithShared");
let memory = new WebAssembly.Memory({
initial: 0, maximum: 10, shared: true});
assertMemoryIsValid(memory);
// Assert that the buffer is frozen when memory is shared.
assertTrue(Object.isFrozen(memory.buffer));
})();
(function TestConstructorWithUndefinedShared() {
print("TestConstructorWithUndefinedShared");
// Maximum = undefined, shared = undefined.
let memory = new WebAssembly.Memory({
initial: 0, maximum: undefined, shared: undefined});
assertMemoryIsValid(memory);
})();
(function TestConstructorWithNumericShared() {
print("TestConstructorWithNumericShared");
// For numeric values, shared = true.
let memory = new WebAssembly.Memory({
initial: 0, maximum: 10, shared: 2098665});
assertMemoryIsValid(memory);
})();
(function TestConstructorWithEmptyStringShared() {
print("TestConstructorWithEmptyStringShared");
// Maximum = undefined, shared = false.
let memory = new WebAssembly.Memory({
initial: 0, maximum: undefined, shared: ""});
assertMemoryIsValid(memory);
})();
(function TestConstructorWithUndefinedMaxShared() {
print("TestConstructorWithUndefinedMaxShared");
// New memory with Maximum = undefined, shared = true => TypeError.
assertThrows(() => new WebAssembly.Memory({initial: 0, shared: true}),
TypeError);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment