From 29041c30dd455931f6402ff57fd4bfbb4d4200f8 Mon Sep 17 00:00:00 2001 From: HyukWoo Park Date: Mon, 15 Jul 2024 14:48:39 +0900 Subject: [PATCH] Support SharedArrayBuffer in WASM Memory Signed-off-by: HyukWoo Park --- build/config.cmake | 4 + build/escargot.cmake | 1 + src/Escargot.h | 2 +- src/api/EscargotPublic.cpp | 4 +- src/api/EscargotPublic.h | 2 +- src/runtime/ArrayBufferObject.cpp | 23 ++++ src/runtime/ArrayBufferObject.h | 2 + src/runtime/BackingStore.cpp | 26 ++-- src/runtime/BackingStore.h | 32 ++++- src/runtime/Platform.h | 2 +- src/runtime/SharedArrayBufferObject.cpp | 20 +++ src/runtime/SharedArrayBufferObject.h | 3 + src/runtime/StaticStrings.h | 1 + src/wasm/BuiltinWASM.cpp | 161 ++++++++++++++++++------ src/wasm/WASMObject.cpp | 35 +++--- src/wasm/WASMObject.h | 10 +- 16 files changed, 246 insertions(+), 82 deletions(-) diff --git a/build/config.cmake b/build/config.cmake index 3523a1d75..f4d826a78 100644 --- a/build/config.cmake +++ b/build/config.cmake @@ -138,6 +138,10 @@ ENDIF() IF (ESCARGOT_WASM) SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_WASM) + IF (NOT DEFINED ESCARGOT_THREADING) + # threading should be enabled for WASM (WASM threading feature) + SET (ESCARGOT_THREADING ON) + ENDIF() ENDIF() IF (ESCARGOT_THREADING) diff --git a/build/escargot.cmake b/build/escargot.cmake index 961aed5ba..ef578a040 100644 --- a/build/escargot.cmake +++ b/build/escargot.cmake @@ -146,6 +146,7 @@ IF (ESCARGOT_WASM) SET (WALRUS_MODE ${ESCARGOT_MODE}) SET (WALRUS_OUTPUT "shared_lib") SET (WALRUS_WASI OFF) # WASI should be OFF + SET (WALRUS_EXTENDED_FEATURES ON) # enable extended features IF (${ESCARGOT_MODE} STREQUAL "release") SET (WALRUS_CXXFLAGS ${WALRUS_CXXFLAGS} ${ESCARGOT_CXXFLAGS_RELEASE}) diff --git a/src/Escargot.h b/src/Escargot.h index 0850dcde3..e88e102fc 100755 --- a/src/Escargot.h +++ b/src/Escargot.h @@ -260,7 +260,7 @@ if (f.type == Type::B) { puts("failed in msvc."); } #endif // FIXME arm devices raise SIGBUS when using unaligned address to __atomic_* functions -#if (defined(COMPILER_GCC) || defined(COMPILER_CLANG)) && !defined(CPU_ARM32) && !defined(CPU_ARM64) +#if defined(COMPILER_GCC) && !defined(CPU_ARM32) && !defined(CPU_ARM64) #define HAVE_BUILTIN_ATOMIC_FUNCTIONS #endif diff --git a/src/api/EscargotPublic.cpp b/src/api/EscargotPublic.cpp index 83609e24f..102d4084d 100644 --- a/src/api/EscargotPublic.cpp +++ b/src/api/EscargotPublic.cpp @@ -129,9 +129,9 @@ class PlatformBridge : public Platform { return m_platform->onMallocArrayBufferObjectDataBuffer(sizeInByte); } - virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte) override + virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte, void* deleterData) override { - m_platform->onFreeArrayBufferObjectDataBuffer(buffer, sizeInByte); + m_platform->onFreeArrayBufferObjectDataBuffer(buffer, sizeInByte, deleterData); } virtual void* onReallocArrayBufferObjectDataBuffer(void* oldBuffer, size_t oldSizeInByte, size_t newSizeInByte) override diff --git a/src/api/EscargotPublic.h b/src/api/EscargotPublic.h index ff00eb05a..8045f6fca 100644 --- a/src/api/EscargotPublic.h +++ b/src/api/EscargotPublic.h @@ -2169,7 +2169,7 @@ class ESCARGOT_EXPORT PlatformRef { { return calloc(sizeInByte, 1); } - virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte) + virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte, void* deleterData) { return free(buffer); } diff --git a/src/runtime/ArrayBufferObject.cpp b/src/runtime/ArrayBufferObject.cpp index 648304c2b..a05c6ac69 100644 --- a/src/runtime/ArrayBufferObject.cpp +++ b/src/runtime/ArrayBufferObject.cpp @@ -61,6 +61,20 @@ ArrayBufferObject* ArrayBufferObject::allocateArrayBuffer(ExecutionState& state, return obj; } +ArrayBufferObject* ArrayBufferObject::allocateExternalArrayBuffer(ExecutionState& state, void* dataBlock, size_t byteLength) +{ + if (UNLIKELY(byteLength >= ArrayBuffer::maxArrayBufferSize)) { + ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_InvalidArrayBufferSize); + } + + // creating a fixed length memory buffer from memaddr. + // NOTE) deleter do nothing, dataBlock will be freed in external module + BackingStore* backingStore = BackingStore::createNonSharedBackingStore(dataBlock, byteLength, + [](void* data, size_t length, void* deleterData) {}, nullptr); + + return new ArrayBufferObject(state, backingStore); +} + ArrayBufferObject* ArrayBufferObject::cloneArrayBuffer(ExecutionState& state, ArrayBuffer* srcBuffer, size_t srcByteOffset, uint64_t srcLength, Object* constructor) { // https://www.ecma-international.org/ecma-262/10.0/#sec-clonearraybuffer @@ -84,6 +98,15 @@ ArrayBufferObject::ArrayBufferObject(ExecutionState& state, Object* proto) { } +ArrayBufferObject::ArrayBufferObject(ExecutionState& state, BackingStore* backingStore) + : ArrayBufferObject(state) +{ + // BackingStore should be valid and non-shared + ASSERT(!!backingStore && !backingStore->isShared()); + + updateBackingStore(backingStore); +} + void ArrayBufferObject::allocateBuffer(ExecutionState& state, size_t byteLength) { detachArrayBuffer(); diff --git a/src/runtime/ArrayBufferObject.h b/src/runtime/ArrayBufferObject.h index 2d405e39f..d1177076e 100644 --- a/src/runtime/ArrayBufferObject.h +++ b/src/runtime/ArrayBufferObject.h @@ -31,9 +31,11 @@ class ArrayBufferObject : public ArrayBuffer { public: explicit ArrayBufferObject(ExecutionState& state); explicit ArrayBufferObject(ExecutionState& state, Object* proto); + explicit ArrayBufferObject(ExecutionState& state, BackingStore* backingStore); static ArrayBufferObject* allocateArrayBuffer(ExecutionState& state, Object* constructor, uint64_t byteLength, Optional maxByteLength = Optional(), bool resizeable = true); + static ArrayBufferObject* allocateExternalArrayBuffer(ExecutionState& state, void* dataBlock, size_t byteLength); static ArrayBufferObject* cloneArrayBuffer(ExecutionState& state, ArrayBuffer* srcBuffer, size_t srcByteOffset, uint64_t srcLength, Object* constructor); void allocateBuffer(ExecutionState& state, size_t bytelength); diff --git a/src/runtime/BackingStore.cpp b/src/runtime/BackingStore.cpp index ee2830b14..bf12313a4 100644 --- a/src/runtime/BackingStore.cpp +++ b/src/runtime/BackingStore.cpp @@ -27,7 +27,7 @@ namespace Escargot { static void backingStorePlatformDeleter(void* data, size_t length, void* deleterData) { if (!!data) { - Global::platform()->onFreeArrayBufferObjectDataBuffer(data, length); + Global::platform()->onFreeArrayBufferObjectDataBuffer(data, length, deleterData); } } @@ -46,15 +46,15 @@ BackingStore* BackingStore::createDefaultResizableNonSharedBackingStore(size_t b byteLength, backingStorePlatformDeleter, maxByteLength, true); } -BackingStore* BackingStore::createNonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback callback, void* callbackData) +BackingStore* BackingStore::createNonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback deleter, void* callbackData) { - return new NonSharedBackingStore(data, byteLength, callback, callbackData, false); + return new NonSharedBackingStore(data, byteLength, deleter, callbackData, false); } -NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback callback, void* callbackData, bool isAllocatedByPlatform) +NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback deleter, void* callbackData, bool isAllocatedByPlatform) : m_data(data) , m_byteLength(byteLength) - , m_deleter(callback) + , m_deleter(deleter) , m_deleterData(callbackData) , m_isAllocatedByPlatform(isAllocatedByPlatform) , m_isResizable(false) @@ -67,10 +67,10 @@ NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, Back nullptr, nullptr, nullptr); } -NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback callback, size_t maxByteLength, bool isAllocatedByPlatform) +NonSharedBackingStore::NonSharedBackingStore(void* data, size_t byteLength, BackingStoreDeleterCallback deleter, size_t maxByteLength, bool isAllocatedByPlatform) : m_data(data) , m_byteLength(byteLength) - , m_deleter(callback) + , m_deleter(deleter) , m_maxByteLength(maxByteLength) , m_isAllocatedByPlatform(isAllocatedByPlatform) , m_isResizable(true) @@ -138,7 +138,8 @@ BackingStore* BackingStore::createDefaultSharedBackingStore(size_t byteLength) { SharedDataBlockInfo* sharedInfo = new SharedDataBlockInfo( Global::platform()->onMallocArrayBufferObjectDataBuffer(byteLength), - byteLength); + byteLength, + backingStorePlatformDeleter); return new SharedBackingStore(sharedInfo); } @@ -146,13 +147,14 @@ BackingStore* BackingStore::createDefaultGrowableSharedBackingStore(size_t byteL { SharedDataBlockInfo* sharedInfo = new GrowableSharedDataBlockInfo( Global::platform()->onMallocArrayBufferObjectDataBuffer(maxByteLength), - byteLength, maxByteLength); + byteLength, maxByteLength, + backingStorePlatformDeleter); return new SharedBackingStore(sharedInfo); } BackingStore* BackingStore::createSharedBackingStore(SharedDataBlockInfo* sharedInfo) { - ASSERT(sharedInfo->hasValidReference()); + // ASSERT(sharedInfo->hasValidReference()); return new SharedBackingStore(sharedInfo); } @@ -163,9 +165,9 @@ void SharedDataBlockInfo::deref() auto oldValue = m_refCount.fetch_sub(1); if (oldValue == 1) { if (isGrowable()) { - Global::platform()->onFreeArrayBufferObjectDataBuffer(m_data, maxByteLength()); + m_deleter(m_data, maxByteLength(), nullptr); } else { - Global::platform()->onFreeArrayBufferObjectDataBuffer(m_data, m_byteLength); + m_deleter(m_data, m_byteLength, nullptr); } m_data = nullptr; diff --git a/src/runtime/BackingStore.h b/src/runtime/BackingStore.h index 8ae5c6347..8cea36a68 100644 --- a/src/runtime/BackingStore.h +++ b/src/runtime/BackingStore.h @@ -136,7 +136,7 @@ class BackingStore : public gc, public BufferAddressObserverManager m_byteLength; std::atomic m_refCount; + BackingStoreDeleterCallback m_deleter; }; class GrowableSharedDataBlockInfo : public SharedDataBlockInfo { public: - GrowableSharedDataBlockInfo(void* data, size_t byteLength, size_t maxByteLength) - : SharedDataBlockInfo(data, byteLength) + GrowableSharedDataBlockInfo(void* data, size_t byteLength, size_t maxByteLength, BackingStoreDeleterCallback deleter) + : SharedDataBlockInfo(data, byteLength, deleter) , m_maxByteLength(maxByteLength) { } @@ -368,6 +383,11 @@ class SharedBackingStore : public BackingStore { return m_sharedDataBlockInfo->isGrowable(); } + virtual size_t byteLengthRMW(size_t newByteLength) override + { + return m_sharedDataBlockInfo->byteLengthRMW(newByteLength); + } + virtual void resize(size_t newByteLength) override; void* operator new(size_t size); diff --git a/src/runtime/Platform.h b/src/runtime/Platform.h index 2ed153a03..e0895bf55 100644 --- a/src/runtime/Platform.h +++ b/src/runtime/Platform.h @@ -32,7 +32,7 @@ class Platform { virtual ~Platform() {} // ArrayBuffer virtual void* onMallocArrayBufferObjectDataBuffer(size_t sizeInByte) = 0; - virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte) = 0; + virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte, void* deleterData) = 0; virtual void* onReallocArrayBufferObjectDataBuffer(void* oldBuffer, size_t oldSizeInByte, size_t newSizeInByte) = 0; // Promise diff --git a/src/runtime/SharedArrayBufferObject.cpp b/src/runtime/SharedArrayBufferObject.cpp index 67c82a7c7..ed29a8573 100644 --- a/src/runtime/SharedArrayBufferObject.cpp +++ b/src/runtime/SharedArrayBufferObject.cpp @@ -106,6 +106,20 @@ SharedArrayBufferObject* SharedArrayBufferObject::allocateSharedArrayBuffer(Exec return new SharedArrayBufferObject(state, proto, byteLength); } +SharedArrayBufferObject* SharedArrayBufferObject::allocateExternalSharedArrayBuffer(ExecutionState& state, void* dataBlock, size_t byteLength) +{ + if (UNLIKELY(byteLength >= ArrayBuffer::maxArrayBufferSize)) { + ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().SharedArrayBuffer.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_InvalidArrayBufferSize); + } + + // creating a fixed length memory buffer from memaddr. + // NOTE) deleter do nothing, dataBlock will be freed in external module + SharedDataBlockInfo* sharedInfo = new SharedDataBlockInfo(dataBlock, byteLength, + [](void* data, size_t length, void* deleterData) {}); + + return new SharedArrayBufferObject(state, state.context()->globalObject()->sharedArrayBufferPrototype(), sharedInfo); +} + void* SharedArrayBufferObject::operator new(size_t size) { static MAY_THREAD_LOCAL bool typeInited = false; @@ -272,6 +286,12 @@ void SharedArrayBufferObject::setValueInBuffer(ExecutionState& state, size_t byt } #endif } + +size_t SharedArrayBufferObject::byteLengthRMW(size_t newByteLength) +{ + ASSERT(m_backingStore.hasValue()); + return m_backingStore->byteLengthRMW(newByteLength); +} } // namespace Escargot #endif diff --git a/src/runtime/SharedArrayBufferObject.h b/src/runtime/SharedArrayBufferObject.h index ffb74bfbb..628f5a31c 100644 --- a/src/runtime/SharedArrayBufferObject.h +++ b/src/runtime/SharedArrayBufferObject.h @@ -34,6 +34,7 @@ class SharedArrayBufferObject : public ArrayBuffer { SharedArrayBufferObject(ExecutionState& state, Object* proto, SharedDataBlockInfo* sharedInfo); static SharedArrayBufferObject* allocateSharedArrayBuffer(ExecutionState& state, Object* constructor, uint64_t byteLength, Optional maxByteLength); + static SharedArrayBufferObject* allocateExternalSharedArrayBuffer(ExecutionState& state, void* dataBlock, size_t byteLength); virtual bool isSharedArrayBufferObject() const override { @@ -47,6 +48,8 @@ class SharedArrayBufferObject : public ArrayBuffer { void* operator new(size_t size); void* operator new[](size_t size) = delete; + + size_t byteLengthRMW(size_t newByteLength); }; } // namespace Escargot diff --git a/src/runtime/StaticStrings.h b/src/runtime/StaticStrings.h index b9b8862b3..772c7cc21 100644 --- a/src/runtime/StaticStrings.h +++ b/src/runtime/StaticStrings.h @@ -525,6 +525,7 @@ namespace Escargot { F(memory) \ F(module) \ F(table) \ + F(shared) \ F(v128) \ F(validate) #else diff --git a/src/wasm/BuiltinWASM.cpp b/src/wasm/BuiltinWASM.cpp index d54304157..783121e33 100644 --- a/src/wasm/BuiltinWASM.cpp +++ b/src/wasm/BuiltinWASM.cpp @@ -28,6 +28,7 @@ #include "runtime/ExtendedNativeFunctionObject.h" #include "runtime/ArrayObject.h" #include "runtime/ArrayBufferObject.h" +#include "runtime/SharedArrayBufferObject.h" #include "runtime/BackingStore.h" #include "runtime/PromiseObject.h" #include "runtime/Job.h" @@ -382,6 +383,36 @@ static Value builtinWASMInstanceExportsGetter(ExecutionState& state, Value thisV } // WebAssembly.Memory + +static ArrayBuffer* createFixedLengthMemoryBuffer(ExecutionState& state, wasm_memory_t* memaddr) +{ + ASSERT(!!memaddr); + + // Note) wasm_memory_data with zero size returns null pointer + // predefined temporal address is allocated for this case + void* dataBlock = wasm_memory_size(memaddr) == 0 ? WASMEmptyBlockAddress : wasm_memory_data(memaddr); + + ArrayBuffer* buffer = nullptr; + if (wasm_memory_is_shared(memaddr)) { + // Let block be a Shared Data Block which is identified with the underlying memory of memaddr. + // Let buffer be a new SharedArrayBuffer with the internal slots [[ArrayBufferData]] and [[ArrayBufferByteLength]]. + buffer = SharedArrayBufferObject::allocateExternalSharedArrayBuffer(state, dataBlock, wasm_memory_data_size(memaddr)); + + // Perform ! SetIntegrityLevel(buffer, "frozen"). + if (UNLIKELY(!Object::setIntegrityLevel(state, buffer, false))) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_IllegalFirstArgument); + } + } else { + // Let block be a Data Block which is identified with the underlying memory of memaddr. + // Let buffer be a new ArrayBuffer with the internal slots [[ArrayBufferData]], [[ArrayBufferByteLength]], and [[ArrayBufferDetachKey]]. + buffer = ArrayBufferObject::allocateExternalArrayBuffer(state, dataBlock, wasm_memory_data_size(memaddr)); + } + + + // Return buffer. + return buffer; +} + static Value builtinWASMMemoryConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) { const StaticStrings* strings = &state.context()->staticStrings(); @@ -391,6 +422,7 @@ static Value builtinWASMMemoryConstructor(ExecutionState& state, Value thisValue ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->WebAssembly.string(), false, strings->Memory.string(), ErrorObject::Messages::Not_Invoked_With_New); } + bool isShared = false; Value desc = argv[0]; // check and get 'initial' property from the first argument @@ -406,12 +438,17 @@ static Value builtinWASMMemoryConstructor(ExecutionState& state, Value thisValue uint32_t maximum = wasm_limits_max_default; { auto maxResult = wasmGetValueFromObjectProperty(state, desc, strings->maximum, strings->valueOf); + auto sharedResult = wasmGetValueFromObjectProperty(state, desc, strings->shared, AtomicString()); + isShared = sharedResult.first ? sharedResult.second.toBoolean() : false; + if (maxResult.first) { Value maxValue = maxResult.second; if (!maxValue.isUInt32()) { ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->WebAssembly.string(), false, strings->Memory.string(), ErrorObject::Messages::GlobalObject_IllegalFirstArgument); } maximum = maxValue.asUInt32(); + } else if (isShared) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->WebAssembly.string(), false, strings->Memory.string(), ErrorObject::Messages::GlobalObject_IllegalFirstArgument); } } @@ -424,21 +461,20 @@ static Value builtinWASMMemoryConstructor(ExecutionState& state, Value thisValue own wasm_memorytype_t* memtype = wasm_memorytype_new(&limits); // Let (store, memaddr) be mem_alloc(store, memtype). If allocation fails, throw a RangeError exception. - own wasm_memory_t* memaddr = wasm_memory_new(ThreadLocal::wasmStore(), memtype); + own wasm_memory_t* memaddr = nullptr; + if (isShared) { + memaddr = wasm_shared_memory_new(ThreadLocal::wasmStore(), memtype); + } else { + memaddr = wasm_memory_new(ThreadLocal::wasmStore(), memtype); + } wasm_memorytype_delete(memtype); - wasm_ref_t* memref = wasm_memory_as_ref(memaddr); - // Let map be the surrounding agent's associated Memory object cache. + // Assert: map[memaddr] doesn’t exist. + ASSERT(!state.context()->wasmCache()->findMemory(wasm_memory_as_ref(memaddr))); - // Create a memory buffer from memaddr - ArrayBufferObject* buffer = new ArrayBufferObject(state); - // Note) wasm_memory_data with zero size returns null pointer - // predefined temporal address is allocated for this case - void* dataBlock = initial == 0 ? WASMEmptyBlockAddress : wasm_memory_data(memaddr); - BackingStore* backingStore = BackingStore::createNonSharedBackingStore(dataBlock, wasm_memory_data_size(memaddr), - [](void* data, size_t length, void* deleterData) {}, nullptr); - buffer->attachBuffer(backingStore); + // Let buffer be the result of creating a fixed length memory buffer from memaddr. + ArrayBuffer* buffer = createFixedLengthMemoryBuffer(state, memaddr); // Let proto be ? GetPrototypeFromConstructor(newTarget, "%WebAssemblyMemoryPrototype%"). Object* proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* { @@ -450,7 +486,7 @@ static Value builtinWASMMemoryConstructor(ExecutionState& state, Value thisValue WASMMemoryObject* memoryObj = new WASMMemoryObject(state, proto, memaddr, buffer); // Set map[memaddr] to memory. - state.context()->wasmCache()->appendMemory(memref, memoryObj); + state.context()->wasmCache()->appendMemory(wasm_memory_as_ref(memaddr), memoryObj); return memoryObj; } @@ -474,40 +510,51 @@ static Value builtinWASMMemoryGrow(ExecutionState& state, Value thisValue, size_ WASMMemoryObject* memoryObj = thisValue.asObject()->asWASMMemoryObject(); wasm_memory_t* memaddr = memoryObj->memory(); - // Let ret be the mem_size(store, memaddr). - // wasm_memory_pages_t maps to uint32_t - wasm_memory_pages_t ret = wasm_memory_size(memaddr); + if (memoryObj->buffer()->isSharedArrayBufferObject()) { + if (delta > wasm_memory_max_size(memaddr)) { + ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, strings->WebAssemblyDotMemory.string(), false, strings->grow.string(), ErrorObject::Messages::GlobalObject_RangeError); + } - // Let store be mem_grow(store, memaddr, delta). - bool success = wasm_memory_grow(memaddr, delta); - if (!success) { - // If store is error, throw a RangeError exception. - ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, strings->WebAssemblyDotMemory.string(), false, strings->grow.string(), ErrorObject::Messages::GlobalObject_RangeError); - } + // result of an atomic read-modify-write of the new size to the internal [[ArrayBufferByteLength]] slot. + size_t ret = memoryObj->buffer()->asSharedArrayBufferObject()->byteLengthRMW(delta * MEMORY_PAGE_SIZE); + // The return value is the value in pages read from the internal [[ArrayBufferByteLength]] slot before the modification + return Value(ret / MEMORY_PAGE_SIZE); + } else { + // Let ret be the mem_size(store, memaddr). + // wasm_memory_pages_t maps to uint32_t + wasm_memory_pages_t ret = wasm_memory_size(memaddr); + + // Let store be mem_grow(store, memaddr, delta). + bool success = wasm_memory_grow(memaddr, delta); + if (!success) { + // If store is error, throw a RangeError exception. + ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, strings->WebAssemblyDotMemory.string(), false, strings->grow.string(), ErrorObject::Messages::GlobalObject_RangeError); + } - // Let map be the surrounding agent's associated Memory object cache. - // Assert: map[memaddr] exists. - ASSERT(state.context()->wasmCache()->findMemory(wasm_memory_as_ref(memaddr))); + // Let map be the surrounding agent's associated Memory object cache. + // Assert: map[memaddr] exists. + ASSERT(state.context()->wasmCache()->findMemory(wasm_memory_as_ref(memaddr))); - // Perform ! DetachArrayBuffer(memory.[[BufferObject]], "WebAssembly.Memory"). - memoryObj->buffer()->detachArrayBuffer(); + // Perform ! DetachArrayBuffer(memory.[[BufferObject]], "WebAssembly.Memory"). + memoryObj->buffer()->asArrayBufferObject()->detachArrayBuffer(); - // Let buffer be a the result of creating a memory buffer from memaddr. - ArrayBufferObject* buffer = new ArrayBufferObject(state); - // Note) wasm_memory_data with zero size returns null pointer - // predefined temporal address is allocated for this case - size_t dataSize = wasm_memory_data_size(memaddr); - void* dataBlock = dataSize == 0 ? WASMEmptyBlockAddress : wasm_memory_data(memaddr); + // Let buffer be a the result of creating a memory buffer from memaddr. + ArrayBufferObject* buffer = new ArrayBufferObject(state); + // Note) wasm_memory_data with zero size returns null pointer + // predefined temporal address is allocated for this case + size_t dataSize = wasm_memory_data_size(memaddr); + void* dataBlock = dataSize == 0 ? WASMEmptyBlockAddress : wasm_memory_data(memaddr); - BackingStore* backingStore = BackingStore::createNonSharedBackingStore(dataBlock, dataSize, - [](void* data, size_t length, void* deleterData) {}, nullptr); - buffer->attachBuffer(backingStore); + BackingStore* backingStore = BackingStore::createNonSharedBackingStore(dataBlock, dataSize, + [](void* data, size_t length, void* deleterData) {}, nullptr); + buffer->attachBuffer(backingStore); - // Set memory.[[BufferObject]] to buffer. - memoryObj->setBuffer(buffer); + // Set memory.[[BufferObject]] to buffer. + memoryObj->setBuffer(buffer); - // Return ret. - return Value(ret); + // Return ret. + return Value(ret); + } } static Value builtinWASMMemoryBufferGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) @@ -517,11 +564,45 @@ static Value builtinWASMMemoryBufferGetter(ExecutionState& state, Value thisValu } WASMMemoryObject* memoryObj = thisValue.asObject()->asWASMMemoryObject(); + + if (memoryObj->buffer()->isSharedArrayBufferObject()) { + wasm_memory_t* memaddr = memoryObj->memory(); + ASSERT(wasm_memory_is_shared(memaddr)); + + // Let newByteLength be the byte length of M.[[Memory]]. + size_t newByteLength = wasm_memory_data_size(memaddr); + // Let oldByteLength be M.[[BufferObject]].[[ArrayBufferByteLength]]. + size_t oldByteLength = memoryObj->buffer()->byteLength(); + + // If newByteLength is equal to oldByteLength, then return M.[[BufferObject]]. + if (newByteLength == oldByteLength) { + return memoryObj->buffer(); + } + + // Let buffer be a new SharedArrayBuffer whose [[ArrayBufferData]] aliases M.[[Memory]] and whose [[ArrayBufferByteLength]] is set to newByteLength. + // Note) wasm_memory_data with zero size returns null pointer + // predefined temporal address is allocated for this case + void* dataBlock = newByteLength == 0 ? WASMEmptyBlockAddress : wasm_memory_data(memaddr); + ArrayBuffer* buffer = SharedArrayBufferObject::allocateExternalSharedArrayBuffer(state, dataBlock, newByteLength); + ; + + // Let status be SetIntegrityLevel(buffer, "frozen"). + // If status is false, throw a TypeError exception. + if (Object::setIntegrityLevel(state, buffer, false)) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_IllegalFirstArgument); + } + + // Set M.[[BufferObject]] to buffer. + memoryObj->setBuffer(buffer); + // Return buffer. + return buffer; + } + if (UNLIKELY(wasm_memory_data_size(memoryObj->memory()) != memoryObj->buffer()->byteLength())) { // FIXME data block of memory has been changed by previous memory.grow operation, but not yet reflected // So, we update the buffer here to reflect modifications on data block and its size. // TODO Actually, this change should be applied immediately after memory.grow operation - memoryObj->buffer()->detachArrayBuffer(); + memoryObj->buffer()->asArrayBufferObject()->detachArrayBuffer(); ArrayBufferObject* buffer = new ArrayBufferObject(state); size_t dataSize = wasm_memory_data_size(memoryObj->memory()); diff --git a/src/wasm/WASMObject.cpp b/src/wasm/WASMObject.cpp index 4ce68b4fd..114a0f527 100644 --- a/src/wasm/WASMObject.cpp +++ b/src/wasm/WASMObject.cpp @@ -24,6 +24,7 @@ #include "runtime/Context.h" #include "runtime/Object.h" #include "runtime/ArrayBufferObject.h" +#include "runtime/SharedArrayBufferObject.h" #include "runtime/BackingStore.h" #include "wasm/WASMObject.h" #include "wasm/WASMValueConverter.h" @@ -125,12 +126,12 @@ void* WASMInstanceObject::operator new(size_t size) return GC_MALLOC_EXPLICITLY_TYPED(size, descr); } -WASMMemoryObject::WASMMemoryObject(ExecutionState& state, wasm_memory_t* memory, ArrayBufferObject* buffer) +WASMMemoryObject::WASMMemoryObject(ExecutionState& state, wasm_memory_t* memory, ArrayBuffer* buffer) : WASMMemoryObject(state, state.context()->globalObject()->wasmMemoryPrototype(), memory, buffer) { } -WASMMemoryObject::WASMMemoryObject(ExecutionState& state, Object* proto, wasm_memory_t* memory, ArrayBufferObject* buffer) +WASMMemoryObject::WASMMemoryObject(ExecutionState& state, Object* proto, wasm_memory_t* memory, ArrayBuffer* buffer) : DerivedObject(state, proto) , m_memory(memory) , m_buffer(buffer) @@ -140,7 +141,9 @@ WASMMemoryObject::WASMMemoryObject(ExecutionState& state, Object* proto, wasm_me addFinalizer([](PointerValue* obj, void* data) { WASMMemoryObject* self = (WASMMemoryObject*)obj; wasm_memory_delete(self->memory()); - self->buffer()->detachArrayBuffer(); + if (!self->buffer()->isSharedArrayBufferObject()) { + self->buffer()->asArrayBufferObject()->detachArrayBuffer(); + } }, nullptr); } @@ -171,20 +174,24 @@ WASMMemoryObject* WASMMemoryObject::createMemoryObject(ExecutionState& state, wa return memory; } - // Let memory be a new Memory. - // Initialize memory from memory. - ArrayBufferObject* buffer = new ArrayBufferObject(state); - // Note) wasm_memory_data with zero size returns null pointer // predefined temporal address is allocated for this case void* dataBlock = wasm_memory_size(memaddr) == 0 ? WASMEmptyBlockAddress : wasm_memory_data(memaddr); - // Init BackingStore with empty deleter - BackingStore* backingStore = BackingStore::createNonSharedBackingStore(dataBlock, wasm_memory_data_size(memaddr), - [](void* data, size_t length, void* deleterData) {}, nullptr); - buffer->attachBuffer(backingStore); + ArrayBuffer* buffer = nullptr; + if (wasm_memory_is_shared(memaddr)) { + buffer = SharedArrayBufferObject::allocateExternalSharedArrayBuffer(state, dataBlock, wasm_memory_data_size(memaddr)); + } else { + buffer = ArrayBufferObject::allocateExternalArrayBuffer(state, dataBlock, wasm_memory_data_size(memaddr)); + } + + // Let status be the result of calling SetIntegrityLevel(buffer, "frozen"). + // If status is false, a TypeError is thrown. + if (!Object::setIntegrityLevel(state, buffer, false)) { + ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_IllegalFirstArgument); + } - // Set memory.[[Memory]] to memory. + // Set memory.[[Memory]] to memaddr. // Set memory.[[BufferObject]] to buffer. memory = new WASMMemoryObject(state, memaddr, buffer); @@ -195,13 +202,13 @@ WASMMemoryObject* WASMMemoryObject::createMemoryObject(ExecutionState& state, wa return memory; } -ArrayBufferObject* WASMMemoryObject::buffer() const +ArrayBuffer* WASMMemoryObject::buffer() const { ASSERT(!!m_buffer && !m_buffer->isDetachedBuffer()); return m_buffer; } -void WASMMemoryObject::setBuffer(ArrayBufferObject* buffer) +void WASMMemoryObject::setBuffer(ArrayBuffer* buffer) { ASSERT(!!buffer && !buffer->isDetachedBuffer()); m_buffer = buffer; diff --git a/src/wasm/WASMObject.h b/src/wasm/WASMObject.h index 50f289704..dc539a397 100644 --- a/src/wasm/WASMObject.h +++ b/src/wasm/WASMObject.h @@ -99,8 +99,8 @@ class WASMInstanceObject : public DerivedObject { class WASMMemoryObject : public DerivedObject { public: - explicit WASMMemoryObject(ExecutionState& state, wasm_memory_t* memory, ArrayBufferObject* buffer); - explicit WASMMemoryObject(ExecutionState& state, Object* proto, wasm_memory_t* memory, ArrayBufferObject* buffer); + explicit WASMMemoryObject(ExecutionState& state, wasm_memory_t* memory, ArrayBuffer* buffer); + explicit WASMMemoryObject(ExecutionState& state, Object* proto, wasm_memory_t* memory, ArrayBuffer* buffer); virtual bool isWASMMemoryObject() const override { @@ -118,12 +118,12 @@ class WASMMemoryObject : public DerivedObject { return m_memory; } - ArrayBufferObject* buffer() const; - void setBuffer(ArrayBufferObject* buffer); + ArrayBuffer* buffer() const; + void setBuffer(ArrayBuffer* buffer); private: wasm_memory_t* m_memory; - ArrayBufferObject* m_buffer; + ArrayBuffer* m_buffer; }; class WASMTableObject : public DerivedObject {