[wasm][anyref] Allow anyref values in WebAssembly.Global objects

This CL adds support for anyref in WebAssembly.Global objects. Note
that the specification is not complete yet in this area.

I did the following changes:
- I renamed the `array_buffer` field of WasmGlobalObject to
  `untagged_buffer`
- I added an additional field of type FixedArray, `tagged_buffer`.
  - In the constructor of WasmGlobalObject I allocate either the former
    or the latter, but not both.
- In the WebAssembly.Global constructor I added special handling for
  the case where no initial value is provided. In that case I set the
  inital value to `null` and not to `undefined`.

R=titzer@chromium.org

Bug: v8:7581
Change-Id: I7e4855d7e6c04a9bcdc7ebd450caca5819d060e2
Reviewed-on: https://chromium-review.googlesource.com/c/1398226
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58625}
This commit is contained in:
Andreas Haas 2019-01-08 12:16:53 +01:00 committed by Commit Bot
parent 195686d37c
commit 741f2312d1
6 changed files with 133 additions and 40 deletions

View File

@ -1792,7 +1792,7 @@ bool InstanceBuilder::ProcessImportedWasmGlobalObject(
return false;
}
if (global.mutability) {
Handle<JSArrayBuffer> buffer(global_object->array_buffer(), isolate_);
Handle<JSArrayBuffer> buffer(global_object->untagged_buffer(), isolate_);
int index = (*next_imported_mutable_global_index)++;
instance->imported_mutable_globals_buffers()->set(index, *buffer);
// It is safe in this case to store the raw pointer to the buffer
@ -2193,33 +2193,35 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
case kExternalGlobal: {
const WasmGlobal& global = module_->globals[exp.index];
if (enabled_.mut_global) {
Handle<JSArrayBuffer> buffer;
Handle<JSArrayBuffer> untagged_buffer;
uint32_t offset;
if (global.mutability && global.imported) {
Handle<FixedArray> buffers_array(
instance->imported_mutable_globals_buffers(), isolate_);
buffer = buffers_array->GetValueChecked<JSArrayBuffer>(
untagged_buffer = buffers_array->GetValueChecked<JSArrayBuffer>(
isolate_, global.index);
Address global_addr =
instance->imported_mutable_globals()[global.index];
size_t buffer_size = buffer->byte_length();
size_t buffer_size = untagged_buffer->byte_length();
Address backing_store =
reinterpret_cast<Address>(buffer->backing_store());
reinterpret_cast<Address>(untagged_buffer->backing_store());
CHECK(global_addr >= backing_store &&
global_addr < backing_store + buffer_size);
offset = static_cast<uint32_t>(global_addr - backing_store);
} else {
buffer = handle(instance->untagged_globals_buffer(), isolate_);
untagged_buffer =
handle(instance->untagged_globals_buffer(), isolate_);
offset = global.offset;
}
// Since the global's array buffer is always provided, allocation
// should never fail.
// Since the global's array untagged_buffer is always provided,
// allocation should never fail.
Handle<WasmGlobalObject> global_obj =
WasmGlobalObject::New(isolate_, buffer, global.type, offset,
global.mutability)
WasmGlobalObject::New(isolate_, untagged_buffer,
MaybeHandle<FixedArray>(), global.type,
offset, global.mutability)
.ToHandleChecked();
desc.set_value(global_obj);
} else {

View File

@ -1152,6 +1152,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
type = i::wasm::kWasmI64;
} else if (string->StringEquals(v8_str(isolate, "f64"))) {
type = i::wasm::kWasmF64;
} else if (string->StringEquals(v8_str(isolate, "anyref"))) {
type = i::wasm::kWasmAnyRef;
} else {
thrower.TypeError(
"Descriptor property 'value' must be 'i32', 'i64', 'f32', or "
@ -1163,7 +1165,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
const uint32_t offset = 0;
i::MaybeHandle<i::WasmGlobalObject> maybe_global_obj =
i::WasmGlobalObject::New(i_isolate, i::MaybeHandle<i::JSArrayBuffer>(),
type, offset, is_mutable);
i::MaybeHandle<i::FixedArray>(), type, offset,
is_mutable);
i::Handle<i::WasmGlobalObject> global_obj;
if (!maybe_global_obj.ToHandle(&global_obj)) {
@ -1222,6 +1225,17 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
global_obj->SetF64(f64_value);
break;
}
case i::wasm::kWasmAnyRef: {
if (args.Length() < 2) {
// When no inital value is provided, we have to use the WebAssembly
// default value 'null', and not the JS default value 'undefined'.
global_obj->SetAnyRef(
handle(i::ReadOnlyRoots(i_isolate).null_value(), i_isolate));
break;
}
global_obj->SetAnyRef(Utils::OpenHandle(*value));
break;
}
default:
UNREACHABLE();
}
@ -1469,6 +1483,9 @@ void WebAssemblyGlobalGetValueCommon(
case i::wasm::kWasmF64:
return_value.Set(receiver->GetF64());
break;
case i::wasm::kWasmAnyRef:
return_value.Set(Utils::ToLocal(receiver->GetAnyRef()));
break;
default:
UNREACHABLE();
}
@ -1530,6 +1547,10 @@ void WebAssemblyGlobalSetValue(
receiver->SetF64(f64_value);
break;
}
case i::wasm::kWasmAnyRef: {
receiver->SetAnyRef(Utils::OpenHandle(*args[0]));
break;
}
default:
UNREACHABLE();
}

View File

@ -120,7 +120,9 @@ OPTIONAL_ACCESSORS2(WasmMemoryObject, instances, WeakArrayList,
kInstancesOffset)
// WasmGlobalObject
ACCESSORS2(WasmGlobalObject, array_buffer, JSArrayBuffer, kArrayBufferOffset)
ACCESSORS2(WasmGlobalObject, untagged_buffer, JSArrayBuffer,
kUntaggedBufferOffset)
ACCESSORS2(WasmGlobalObject, tagged_buffer, FixedArray, kTaggedBufferOffset)
SMI_ACCESSORS(WasmGlobalObject, offset, kOffsetOffset)
SMI_ACCESSORS(WasmGlobalObject, flags, kFlagsOffset)
BIT_FIELD_ACCESSORS(WasmGlobalObject, flags, type, WasmGlobalObject::TypeBits)
@ -132,8 +134,9 @@ int WasmGlobalObject::type_size() const {
}
Address WasmGlobalObject::address() const {
DCHECK_LE(offset() + type_size(), array_buffer()->byte_length());
return Address(array_buffer()->backing_store()) + offset();
DCHECK_NE(type(), wasm::kWasmAnyRef);
DCHECK_LE(offset() + type_size(), untagged_buffer()->byte_length());
return Address(untagged_buffer()->backing_store()) + offset();
}
int32_t WasmGlobalObject::GetI32() {
@ -152,6 +155,11 @@ double WasmGlobalObject::GetF64() {
return ReadLittleEndianValue<double>(address());
}
Handle<Object> WasmGlobalObject::GetAnyRef() {
DCHECK_EQ(type(), wasm::kWasmAnyRef);
return handle(tagged_buffer()->get(offset()), GetIsolate());
}
void WasmGlobalObject::SetI32(int32_t value) {
WriteLittleEndianValue<int32_t>(address(), value);
}
@ -168,6 +176,11 @@ void WasmGlobalObject::SetF64(double value) {
WriteLittleEndianValue<double>(address(), value);
}
void WasmGlobalObject::SetAnyRef(Handle<Object> value) {
DCHECK_EQ(type(), wasm::kWasmAnyRef);
tagged_buffer()->set(offset(), *value);
}
// WasmInstanceObject
PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_start, byte*, kMemoryStartOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_size, size_t, kMemorySizeOffset)

View File

@ -1097,32 +1097,42 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate,
// static
MaybeHandle<WasmGlobalObject> WasmGlobalObject::New(
Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_buffer,
wasm::ValueType type, int32_t offset, bool is_mutable) {
Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_untagged_buffer,
MaybeHandle<FixedArray> maybe_tagged_buffer, wasm::ValueType type,
int32_t offset, bool is_mutable) {
Handle<JSFunction> global_ctor(
isolate->native_context()->wasm_global_constructor(), isolate);
auto global_obj = Handle<WasmGlobalObject>::cast(
isolate->factory()->NewJSObject(global_ctor));
uint32_t type_size = wasm::ValueTypes::ElementSizeInBytes(type);
Handle<JSArrayBuffer> buffer;
if (!maybe_buffer.ToHandle(&buffer)) {
// If no buffer was provided, create one long enough for the given type.
buffer =
isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED);
const bool initialize = true;
if (!JSArrayBuffer::SetupAllocatingData(buffer, isolate, type_size,
initialize)) {
return {};
if (type == wasm::kWasmAnyRef) {
Handle<FixedArray> tagged_buffer;
if (!maybe_tagged_buffer.ToHandle(&tagged_buffer)) {
// If no buffer was provided, create one.
tagged_buffer = isolate->factory()->NewFixedArray(1, TENURED);
CHECK_EQ(offset, 0);
}
global_obj->set_tagged_buffer(*tagged_buffer);
} else {
Handle<JSArrayBuffer> untagged_buffer;
uint32_t type_size = wasm::ValueTypes::ElementSizeInBytes(type);
if (!maybe_untagged_buffer.ToHandle(&untagged_buffer)) {
// If no buffer was provided, create one long enough for the given type.
untagged_buffer =
isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED);
const bool initialize = true;
if (!JSArrayBuffer::SetupAllocatingData(untagged_buffer, isolate,
type_size, initialize)) {
return {};
}
}
// Check that the offset is in bounds.
CHECK_LE(offset + type_size, untagged_buffer->byte_length());
global_obj->set_untagged_buffer(*untagged_buffer);
}
// Check that the offset is in bounds.
CHECK_LE(offset + type_size, buffer->byte_length());
global_obj->set_array_buffer(*buffer);
global_obj->set_flags(0);
global_obj->set_type(type);
global_obj->set_offset(offset);

View File

@ -335,7 +335,8 @@ class WasmGlobalObject : public JSObject {
public:
DECL_CAST2(WasmGlobalObject)
DECL_ACCESSORS2(array_buffer, JSArrayBuffer)
DECL_ACCESSORS2(untagged_buffer, JSArrayBuffer)
DECL_ACCESSORS2(tagged_buffer, FixedArray)
DECL_INT32_ACCESSORS(offset)
DECL_INT_ACCESSORS(flags)
DECL_PRIMITIVE_ACCESSORS(type, wasm::ValueType)
@ -350,10 +351,11 @@ class WasmGlobalObject : public JSObject {
#undef WASM_GLOBAL_OBJECT_FLAGS_BIT_FIELDS
// Layout description.
#define WASM_GLOBAL_OBJECT_FIELDS(V) \
V(kArrayBufferOffset, kTaggedSize) \
V(kOffsetOffset, kTaggedSize) \
V(kFlagsOffset, kTaggedSize) \
#define WASM_GLOBAL_OBJECT_FIELDS(V) \
V(kUntaggedBufferOffset, kTaggedSize) \
V(kTaggedBufferOffset, kTaggedSize) \
V(kOffsetOffset, kTaggedSize) \
V(kFlagsOffset, kTaggedSize) \
V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,
@ -361,7 +363,8 @@ class WasmGlobalObject : public JSObject {
#undef WASM_GLOBAL_OBJECT_FIELDS
V8_EXPORT_PRIVATE static MaybeHandle<WasmGlobalObject> New(
Isolate* isolate, MaybeHandle<JSArrayBuffer> buffer, wasm::ValueType type,
Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_untagged_buffer,
MaybeHandle<FixedArray> maybe_tagged_buffer, wasm::ValueType type,
int32_t offset, bool is_mutable);
inline int type_size() const;
@ -370,11 +373,13 @@ class WasmGlobalObject : public JSObject {
inline int64_t GetI64();
inline float GetF32();
inline double GetF64();
inline Handle<Object> GetAnyRef();
inline void SetI32(int32_t value);
inline void SetI64(int64_t value);
inline void SetF32(float value);
inline void SetF64(double value);
inline void SetAnyRef(Handle<Object> value);
private:
// This function returns the address of the global's data in the

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-anyref --expose-gc
// Flags: --experimental-wasm-anyref --expose-gc --experimental-wasm-mut-global
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
@ -128,3 +128,45 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
Test({q: 14});
Test(print);
})();
(function TestAnyRefGlobalObjectDefaultValue() {
print(arguments.callee.name);
let default_init = new WebAssembly.Global({value: 'anyref', mutable: true});
assertSame(null, default_init.value);
assertSame(null, default_init.valueOf());
})();
(function TestAnyRefGlobalObject() {
print(arguments.callee.name);
function TestGlobal(obj) {
const global = new WebAssembly.Global({value: 'anyref'}, obj);
assertSame(obj, global.value);
assertSame(obj, global.valueOf());
}
TestGlobal(null);
TestGlobal(undefined);
TestGlobal(1663);
TestGlobal("testmyglobal");
TestGlobal({a: 11});
TestGlobal(print);
})();
(function TestAnyRefGlobalObjectSetValue() {
print(arguments.callee.name);
let global = new WebAssembly.Global({value: 'anyref', mutable: true});
function TestGlobal(obj) {
global.value = obj;
assertSame(obj, global.value);
assertSame(obj, global.valueOf());
}
TestGlobal(null);
TestGlobal(undefined);
TestGlobal(1663);
TestGlobal("testmyglobal");
TestGlobal({a: 11});
TestGlobal(print);
})();