зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1717914 - wasm: Add IndexType, decode, validate, and construct. r=lth
This commit repurposes MemoryKind as IndexType, making it a part of Limits. DecodeLimits and GetLimits are updated to decode/convert the index type. The index type for a memory object is stored inside the WasmArrayRawBuffer or SharedArrayRawBuffer for the memory. This is not optimal, but is required so that structured clone of a wasm shared memory retains the index type. Linking is updated to require that the imported memory object has the same index type as specified memory. No validation conditions on the min/max of a memory are changed yet. i64 memories still retain the 32-bit restrictions on memory size/reservations. Differential Revision: https://phabricator.services.mozilla.com/D118645
This commit is contained in:
Родитель
f095d458ea
Коммит
40572aafa3
|
@ -423,12 +423,14 @@ MSG_DEF(JSMSG_WASM_NO_SHMEM_COMPILE, 0, JSEXN_WASMCOMPILEERROR, "shared memory
|
|||
MSG_DEF(JSMSG_WASM_BAD_IMPORT_TYPE, 2, JSEXN_WASMLINKERROR, "import object field '{0}' is not a {1}")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IMPORT_SIG, 2, JSEXN_WASMLINKERROR, "imported function '{0}.{1}' signature mismatch")
|
||||
MSG_DEF(JSMSG_WASM_BAD_EXN_SIG, 2, JSEXN_WASMLINKERROR, "imported exception '{0}.{1}' signature mismatch")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IMP_INDEX, 1, JSEXN_WASMLINKERROR, "imported {0} with incompatible index type")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE, 1, JSEXN_WASMLINKERROR, "imported {0} with incompatible size")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IMP_MAX, 1, JSEXN_WASMLINKERROR, "imported {0} with incompatible maximum size")
|
||||
MSG_DEF(JSMSG_WASM_IMP_SHARED_REQD, 0, JSEXN_WASMLINKERROR, "imported unshared memory but shared required")
|
||||
MSG_DEF(JSMSG_WASM_IMP_SHARED_BANNED, 0, JSEXN_WASMLINKERROR, "imported shared memory but unshared required")
|
||||
MSG_DEF(JSMSG_WASM_BAD_FIT, 2, JSEXN_WASMLINKERROR, "{0} segment does not fit in {1}")
|
||||
MSG_DEF(JSMSG_WASM_NO_SHMEM_LINK, 0, JSEXN_WASMLINKERROR, "shared memory is disabled")
|
||||
MSG_DEF(JSMSG_WASM_NO_MEM64_LINK, 0, JSEXN_WASMLINKERROR, "memory64 is disabled")
|
||||
MSG_DEF(JSMSG_WASM_BAD_GLOB_MUT_LINK, 0, JSEXN_WASMLINKERROR, "imported global mutability mismatch")
|
||||
MSG_DEF(JSMSG_WASM_BAD_GLOB_TYPE_LINK, 0, JSEXN_WASMLINKERROR, "imported global type mismatch")
|
||||
MSG_DEF(JSMSG_WASM_BAD_TBL_TYPE_LINK, 0, JSEXN_WASMLINKERROR, "imported table type mismatch")
|
||||
|
@ -462,6 +464,7 @@ MSG_DEF(JSMSG_WASM_BAD_FUNCREF_VALUE, 0, JSEXN_TYPEERR, "can only pass WebA
|
|||
MSG_DEF(JSMSG_WASM_BAD_EQREF_VALUE, 0, JSEXN_TYPEERR, "can only pass a TypedObject to an eqref")
|
||||
MSG_DEF(JSMSG_WASM_BAD_VAL_TYPE, 0, JSEXN_TYPEERR, "cannot pass v128 to or from JS")
|
||||
MSG_DEF(JSMSG_WASM_BAD_STRING_VAL_TYPE, 0, JSEXN_TYPEERR, "bad value type")
|
||||
MSG_DEF(JSMSG_WASM_BAD_STRING_IDX_TYPE, 0, JSEXN_TYPEERR, "bad index type")
|
||||
MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")
|
||||
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
|
||||
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
|
||||
|
|
|
@ -8,6 +8,8 @@ excluded_tests = [
|
|||
"align.wast",
|
||||
# bulk-memory-operations/issues/133 (data.wast:161)
|
||||
"data.wast",
|
||||
# memory limits can be encoded with more bytes now
|
||||
"binary-leb128.wast",
|
||||
# testing that multiple tables fail (imports.wast:309)
|
||||
"imports.wast",
|
||||
# bulk-memory-operations/issues/133 (linking.wast:206)
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Basic tests around creating and linking memories with i64 indices
|
||||
|
||||
const MaxUint32 = 0xFFFF_FFFF;
|
||||
|
||||
// test the validity of different i64 memory types in validation, compilation,
|
||||
// and the JS-API.
|
||||
function memoryTypeModuleText(shared, initial, max) {
|
||||
return `(module
|
||||
(memory i64 ${initial} ${max !== undefined ? max : ''} ${shared ? `shared` : ''})
|
||||
)`;
|
||||
}
|
||||
function memoryTypeDescriptor(shared, initial, max) {
|
||||
return {
|
||||
index: 'i64',
|
||||
initial,
|
||||
maximum: max,
|
||||
shared,
|
||||
};
|
||||
}
|
||||
function validMemoryType(shared, initial, max) {
|
||||
wasmValidateText(memoryTypeModuleText(shared, initial, max));
|
||||
wasmEvalText(memoryTypeModuleText(shared, initial, max));
|
||||
// TODO: JS-API cannot specify pages above UINT32_MAX
|
||||
if (max <= MaxUint32) {
|
||||
new WebAssembly.Memory(memoryTypeDescriptor(shared, initial, max));
|
||||
}
|
||||
}
|
||||
function invalidMemoryType(shared, initial, max, compileMessage, jsMessage) {
|
||||
wasmFailValidateText(memoryTypeModuleText(shared, initial, max), compileMessage);
|
||||
assertErrorMessage(() => wasmEvalText(memoryTypeModuleText(shared, initial, max)), WebAssembly.CompileError, compileMessage);
|
||||
// TODO: JS-API cannot specify pages above UINT32_MAX
|
||||
if (max === undefined || max <= MaxUint32) {
|
||||
assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(shared, initial, max)), Error, jsMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// valid to define a memory with i64
|
||||
validMemoryType(false, 0);
|
||||
// valid to define max with i64
|
||||
validMemoryType(false, 0, 1);
|
||||
// invalid for min to be greater than max with i64
|
||||
invalidMemoryType(false, 2, 1, /minimum must not be greater than maximum/, /bad Memory maximum size/);
|
||||
// valid to define shared memory with max with i64
|
||||
validMemoryType(true, 1, 2);
|
||||
// invalid to define shared memory without max with i64
|
||||
invalidMemoryType(true, 1, undefined, /maximum length required for shared memory/, /maximum is not specified/);
|
||||
|
||||
// test that linking requires index types to be equal
|
||||
function testLink(importedIndexType, importIndexType) {
|
||||
let imported = new WebAssembly.Memory({
|
||||
index: importedIndexType,
|
||||
initial: 0,
|
||||
});
|
||||
let testModule = `(module
|
||||
(memory (import "" "imported") ${importIndexType} 0)
|
||||
)`;
|
||||
if (importedIndexType === importIndexType) {
|
||||
wasmEvalText(testModule, {"": {imported}});
|
||||
} else {
|
||||
assertErrorMessage(() => wasmEvalText(testModule, {"": {imported}}), WebAssembly.LinkError, /index type/);
|
||||
}
|
||||
}
|
||||
testLink('i64', 'i64');
|
||||
testLink('i32', 'i32');
|
||||
testLink('i64', 'i32');
|
||||
testLink('i32', 'i64');
|
|
@ -0,0 +1 @@
|
|||
|jit-test| --wasm-memory64; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--test-wasm-await-tier2; include:wasm.js; skip-if: !wasmMemory64Enabled()
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -68,6 +68,7 @@
|
|||
|
||||
using JS::ToInt32;
|
||||
|
||||
using js::wasm::IndexType;
|
||||
using js::wasm::Pages;
|
||||
using mozilla::Atomic;
|
||||
using mozilla::CheckedInt;
|
||||
|
@ -629,7 +630,7 @@ void WasmArrayRawBuffer::tryGrowMaxPagesInPlace(Pages deltaMaxPages) {
|
|||
|
||||
/* static */
|
||||
WasmArrayRawBuffer* WasmArrayRawBuffer::AllocateWasm(
|
||||
Pages initialPages, const Maybe<Pages>& maxPages,
|
||||
IndexType indexType, Pages initialPages, const Maybe<Pages>& maxPages,
|
||||
const Maybe<size_t>& mapped) {
|
||||
// Prior code has asserted that initial pages is within our implementation
|
||||
// limits (wasm::MaxMemory32Pages) and we can assume it is a valid size_t.
|
||||
|
@ -659,8 +660,8 @@ WasmArrayRawBuffer* WasmArrayRawBuffer::AllocateWasm(
|
|||
uint8_t* base = reinterpret_cast<uint8_t*>(data) + gc::SystemPageSize();
|
||||
uint8_t* header = base - sizeof(WasmArrayRawBuffer);
|
||||
|
||||
auto rawBuf =
|
||||
new (header) WasmArrayRawBuffer(base, maxPages, mappedSize, numBytes);
|
||||
auto rawBuf = new (header)
|
||||
WasmArrayRawBuffer(indexType, base, maxPages, mappedSize, numBytes);
|
||||
return rawBuf;
|
||||
}
|
||||
|
||||
|
@ -744,7 +745,8 @@ static bool CreateSpecificWasmBuffer32(
|
|||
}
|
||||
#endif
|
||||
|
||||
RawbufT* buffer = RawbufT::AllocateWasm(initialPages, maxPages, mappedSize);
|
||||
RawbufT* buffer = RawbufT::AllocateWasm(memory.limits.indexType, initialPages,
|
||||
maxPages, mappedSize);
|
||||
if (!buffer) {
|
||||
if (useHugeMemory) {
|
||||
WarnNumberASCII(cx, JSMSG_WASM_HUGE_MEMORY_FAILED);
|
||||
|
@ -767,8 +769,8 @@ static bool CreateSpecificWasmBuffer32(
|
|||
|
||||
uint64_t cur = maxPages->value() / 2;
|
||||
for (; Pages(cur) > initialPages; cur /= 2) {
|
||||
buffer =
|
||||
RawbufT::AllocateWasm(initialPages, Some(Pages(cur)), mappedSize);
|
||||
buffer = RawbufT::AllocateWasm(memory.limits.indexType, initialPages,
|
||||
Some(Pages(cur)), mappedSize);
|
||||
if (buffer) {
|
||||
break;
|
||||
}
|
||||
|
@ -838,7 +840,6 @@ static bool CreateSpecificWasmBuffer32(
|
|||
|
||||
bool js::CreateWasmBuffer32(JSContext* cx, const wasm::MemoryDesc& memory,
|
||||
MutableHandleArrayBufferObjectMaybeShared buffer) {
|
||||
MOZ_ASSERT(memory.kind == wasm::MemoryKind::Memory32);
|
||||
MOZ_RELEASE_ASSERT(memory.initialPages() <= wasm::MaxMemory32Pages());
|
||||
MOZ_RELEASE_ASSERT(cx->wasm().haveSignalHandlers);
|
||||
|
||||
|
@ -1003,6 +1004,14 @@ size_t ArrayBufferObject::wasmMappedSize() const {
|
|||
return byteLength();
|
||||
}
|
||||
|
||||
IndexType ArrayBufferObject::wasmIndexType() const {
|
||||
if (isWasm()) {
|
||||
return contents().wasmBuffer()->indexType();
|
||||
}
|
||||
MOZ_ASSERT(isPreparedForAsmJS());
|
||||
return wasm::IndexType::I32;
|
||||
}
|
||||
|
||||
Pages ArrayBufferObject::wasmPages() const {
|
||||
if (isWasm()) {
|
||||
return contents().wasmBuffer()->pages();
|
||||
|
@ -1026,6 +1035,13 @@ size_t js::WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf) {
|
|||
return buf->as<SharedArrayBufferObject>().wasmMappedSize();
|
||||
}
|
||||
|
||||
IndexType js::WasmArrayBufferIndexType(
|
||||
const ArrayBufferObjectMaybeShared* buf) {
|
||||
if (buf->is<ArrayBufferObject>()) {
|
||||
return buf->as<ArrayBufferObject>().wasmIndexType();
|
||||
}
|
||||
return buf->as<SharedArrayBufferObject>().wasmIndexType();
|
||||
}
|
||||
Pages js::WasmArrayBufferPages(const ArrayBufferObjectMaybeShared* buf) {
|
||||
if (buf->is<ArrayBufferObject>()) {
|
||||
return buf->as<ArrayBufferObject>().wasmPages();
|
||||
|
@ -1128,8 +1144,8 @@ bool ArrayBufferObject::wasmMovingGrowToPages(
|
|||
return false;
|
||||
}
|
||||
|
||||
WasmArrayRawBuffer* newRawBuf =
|
||||
WasmArrayRawBuffer::AllocateWasm(newPages, Nothing(), Nothing());
|
||||
WasmArrayRawBuffer* newRawBuf = WasmArrayRawBuffer::AllocateWasm(
|
||||
oldBuf->wasmIndexType(), newPages, Nothing(), Nothing());
|
||||
if (!newRawBuf) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -109,6 +109,8 @@ int32_t LiveMappedBufferCount();
|
|||
|
||||
class ArrayBufferObjectMaybeShared;
|
||||
|
||||
wasm::IndexType WasmArrayBufferIndexType(
|
||||
const ArrayBufferObjectMaybeShared* buf);
|
||||
wasm::Pages WasmArrayBufferPages(const ArrayBufferObjectMaybeShared* buf);
|
||||
mozilla::Maybe<wasm::Pages> WasmArrayBufferMaxPages(
|
||||
const ArrayBufferObjectMaybeShared* buf);
|
||||
|
@ -124,6 +126,9 @@ class ArrayBufferObjectMaybeShared : public NativeObject {
|
|||
// Note: the eventual goal is to remove this from ArrayBuffer and have
|
||||
// (Shared)ArrayBuffers alias memory owned by some wasm::Memory object.
|
||||
|
||||
wasm::IndexType wasmIndexType() const {
|
||||
return WasmArrayBufferIndexType(this);
|
||||
}
|
||||
wasm::Pages wasmPages() const { return WasmArrayBufferPages(this); }
|
||||
mozilla::Maybe<wasm::Pages> wasmMaxPages() const {
|
||||
return WasmArrayBufferMaxPages(this);
|
||||
|
@ -449,6 +454,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared {
|
|||
|
||||
size_t wasmMappedSize() const;
|
||||
|
||||
wasm::IndexType wasmIndexType() const;
|
||||
wasm::Pages wasmPages() const;
|
||||
mozilla::Maybe<wasm::Pages> wasmMaxPages() const;
|
||||
|
||||
|
@ -580,21 +586,26 @@ class MutableWrappedPtrOperations<InnerViewTable, Wrapper>
|
|||
};
|
||||
|
||||
class WasmArrayRawBuffer {
|
||||
wasm::IndexType indexType_;
|
||||
mozilla::Maybe<wasm::Pages> maxPages_;
|
||||
size_t mappedSize_; // Not including the header page
|
||||
size_t length_;
|
||||
|
||||
protected:
|
||||
WasmArrayRawBuffer(uint8_t* buffer,
|
||||
WasmArrayRawBuffer(wasm::IndexType indexType, uint8_t* buffer,
|
||||
const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
size_t mappedSize, size_t length)
|
||||
: maxPages_(maxPages), mappedSize_(mappedSize), length_(length) {
|
||||
: indexType_(indexType),
|
||||
maxPages_(maxPages),
|
||||
mappedSize_(mappedSize),
|
||||
length_(length) {
|
||||
MOZ_ASSERT(buffer == dataPointer());
|
||||
}
|
||||
|
||||
public:
|
||||
static WasmArrayRawBuffer* AllocateWasm(
|
||||
wasm::Pages initialPages, const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
wasm::IndexType indexType, wasm::Pages initialPages,
|
||||
const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
const mozilla::Maybe<size_t>& mappedSize);
|
||||
static void Release(void* mem);
|
||||
|
||||
|
@ -608,6 +619,8 @@ class WasmArrayRawBuffer {
|
|||
dataPtr - sizeof(WasmArrayRawBuffer));
|
||||
}
|
||||
|
||||
wasm::IndexType indexType() const { return indexType_; }
|
||||
|
||||
uint8_t* basePointer() { return dataPointer() - gc::SystemPageSize(); }
|
||||
|
||||
size_t mappedSize() const { return mappedSize_; }
|
||||
|
|
|
@ -49,7 +49,8 @@ static size_t SharedArrayMappedSize(size_t length) {
|
|||
// `wasmMaxPages` must always be something for wasm and nothing for other
|
||||
// users.
|
||||
SharedArrayRawBuffer* SharedArrayRawBuffer::AllocateInternal(
|
||||
size_t length, const Maybe<wasm::Pages>& wasmMaxPages,
|
||||
wasm::IndexType wasmIndexType, size_t length,
|
||||
const Maybe<wasm::Pages>& wasmMaxPages,
|
||||
const Maybe<size_t>& wasmMappedSize) {
|
||||
MOZ_RELEASE_ASSERT(length <= ArrayBufferObject::maxBufferByteLength());
|
||||
|
||||
|
@ -80,25 +81,28 @@ SharedArrayRawBuffer* SharedArrayRawBuffer::AllocateInternal(
|
|||
|
||||
uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + gc::SystemPageSize();
|
||||
uint8_t* base = buffer - sizeof(SharedArrayRawBuffer);
|
||||
SharedArrayRawBuffer* rawbuf = new (base)
|
||||
SharedArrayRawBuffer(buffer, length, wasmMaxPages.valueOr(Pages(0)),
|
||||
computedMappedSize, preparedForWasm);
|
||||
SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(
|
||||
wasmIndexType, buffer, length, wasmMaxPages.valueOr(Pages(0)),
|
||||
computedMappedSize, preparedForWasm);
|
||||
MOZ_ASSERT(rawbuf->length_ == length); // Deallocation needs this
|
||||
return rawbuf;
|
||||
}
|
||||
|
||||
SharedArrayRawBuffer* SharedArrayRawBuffer::Allocate(size_t length) {
|
||||
return SharedArrayRawBuffer::AllocateInternal(length, Nothing(), Nothing());
|
||||
return SharedArrayRawBuffer::AllocateInternal(wasm::IndexType::I32, length,
|
||||
Nothing(), Nothing());
|
||||
}
|
||||
|
||||
SharedArrayRawBuffer* SharedArrayRawBuffer::AllocateWasm(
|
||||
Pages initialPages, const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
wasm::IndexType indexType, Pages initialPages,
|
||||
const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
const mozilla::Maybe<size_t>& mappedSize) {
|
||||
// Prior code has asserted that initial pages is within our implementation
|
||||
// limits (wasm::MaxMemory32Pages) and we can assume it is a valid size_t.
|
||||
MOZ_ASSERT(initialPages.hasByteLength());
|
||||
size_t length = initialPages.byteLength();
|
||||
return SharedArrayRawBuffer::AllocateInternal(length, maxPages, mappedSize);
|
||||
return SharedArrayRawBuffer::AllocateInternal(indexType, length, maxPages,
|
||||
mappedSize);
|
||||
}
|
||||
|
||||
void SharedArrayRawBuffer::tryGrowMaxPagesInPlace(Pages deltaMaxPages) {
|
||||
|
|
|
@ -52,6 +52,8 @@ class SharedArrayRawBuffer {
|
|||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount_;
|
||||
mozilla::Atomic<size_t, mozilla::SequentiallyConsistent> length_;
|
||||
Mutex growLock_;
|
||||
// The index type of this buffer if it is a wasm buffer.
|
||||
wasm::IndexType wasmIndexType_;
|
||||
// The maximum size of this buffer in wasm pages. If this buffer was not
|
||||
// prepared for wasm, then this is zero.
|
||||
wasm::Pages wasmMaxPages_;
|
||||
|
@ -69,11 +71,13 @@ class SharedArrayRawBuffer {
|
|||
}
|
||||
|
||||
protected:
|
||||
SharedArrayRawBuffer(uint8_t* buffer, size_t length, wasm::Pages wasmMaxPages,
|
||||
SharedArrayRawBuffer(wasm::IndexType wasmIndexType, uint8_t* buffer,
|
||||
size_t length, wasm::Pages wasmMaxPages,
|
||||
size_t mappedSize, bool preparedForWasm)
|
||||
: refcount_(1),
|
||||
length_(length),
|
||||
growLock_(mutexid::SharedArrayGrow),
|
||||
wasmIndexType_(wasmIndexType),
|
||||
wasmMaxPages_(wasmMaxPages),
|
||||
mappedSize_(mappedSize),
|
||||
preparedForWasm_(preparedForWasm),
|
||||
|
@ -85,7 +89,8 @@ class SharedArrayRawBuffer {
|
|||
// `wasmMaxPages` must always be something for wasm and nothing for other
|
||||
// users.
|
||||
static SharedArrayRawBuffer* AllocateInternal(
|
||||
size_t length, const mozilla::Maybe<wasm::Pages>& wasmMaxPages,
|
||||
wasm::IndexType wasmIndexType, size_t length,
|
||||
const mozilla::Maybe<wasm::Pages>& wasmMaxPages,
|
||||
const mozilla::Maybe<size_t>& wasmMappedSize);
|
||||
|
||||
public:
|
||||
|
@ -104,7 +109,8 @@ class SharedArrayRawBuffer {
|
|||
|
||||
static SharedArrayRawBuffer* Allocate(size_t length);
|
||||
static SharedArrayRawBuffer* AllocateWasm(
|
||||
wasm::Pages initialPages, const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
wasm::IndexType indexType, wasm::Pages initialPages,
|
||||
const mozilla::Maybe<wasm::Pages>& maxPages,
|
||||
const mozilla::Maybe<size_t>& mappedSize);
|
||||
|
||||
// This may be called from multiple threads. The caller must take
|
||||
|
@ -126,6 +132,8 @@ class SharedArrayRawBuffer {
|
|||
dataPtr - sizeof(SharedArrayRawBuffer));
|
||||
}
|
||||
|
||||
wasm::IndexType wasmIndexType() const { return wasmIndexType_; }
|
||||
|
||||
size_t volatileByteLength() const { return length_; }
|
||||
|
||||
wasm::Pages volatileWasmPages() const {
|
||||
|
|
|
@ -2065,7 +2065,8 @@ class MOZ_STACK_CLASS ModuleValidator : public ModuleValidatorShared {
|
|||
: Shareable::False;
|
||||
limits.initial = memory_.minPages();
|
||||
limits.maximum = Nothing();
|
||||
moduleEnv_.memory = Some(MemoryDesc(MemoryKind::Memory32, limits));
|
||||
limits.indexType = IndexType::I32;
|
||||
moduleEnv_.memory = Some(MemoryDesc(limits));
|
||||
}
|
||||
MOZ_ASSERT(moduleEnv_.funcs.empty());
|
||||
if (!moduleEnv_.funcs.resize(funcImportMap_.count() + funcDefs_.length())) {
|
||||
|
|
|
@ -182,13 +182,22 @@ enum class DefinitionKind {
|
|||
|
||||
enum class GlobalTypeImmediate { IsMutable = 0x1, AllowedMask = 0x1 };
|
||||
|
||||
enum class MemoryTableFlags {
|
||||
enum class LimitsFlags {
|
||||
Default = 0x0,
|
||||
HasMaximum = 0x1,
|
||||
IsShared = 0x2,
|
||||
IsI64 = 0x4,
|
||||
};
|
||||
|
||||
enum class MemoryMasks { AllowUnshared = 0x1, AllowShared = 0x3 };
|
||||
enum class LimitsMask {
|
||||
Table = uint8_t(LimitsFlags::HasMaximum),
|
||||
#ifdef ENABLE_WASM_MEMORY64
|
||||
Memory = uint8_t(LimitsFlags::HasMaximum) | uint8_t(LimitsFlags::IsShared) |
|
||||
uint8_t(LimitsFlags::IsI64),
|
||||
#else
|
||||
Memory = uint8_t(LimitsFlags::HasMaximum) | uint8_t(LimitsFlags::IsShared),
|
||||
#endif
|
||||
};
|
||||
|
||||
enum class DataSegmentKind {
|
||||
Active = 0x00,
|
||||
|
|
|
@ -862,13 +862,15 @@ static bool EnforceRangeU32(JSContext* cx, HandleValue v, const char* kind,
|
|||
}
|
||||
|
||||
static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
|
||||
const char* kind, Limits* limits, Shareable allowShared) {
|
||||
LimitsKind kind, Limits* limits) {
|
||||
JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
|
||||
if (!initialAtom) {
|
||||
return false;
|
||||
}
|
||||
RootedId initialId(cx, AtomToId(initialAtom));
|
||||
|
||||
const char* noun = (kind == LimitsKind::Memory ? "Memory" : "Table");
|
||||
|
||||
RootedValue initialVal(cx);
|
||||
if (!GetProperty(cx, obj, obj, initialId, &initialVal)) {
|
||||
return false;
|
||||
|
@ -876,14 +878,14 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
|
|||
|
||||
uint32_t initial = 0;
|
||||
if (!initialVal.isUndefined() &&
|
||||
!EnforceRangeU32(cx, initialVal, kind, "initial size", &initial)) {
|
||||
!EnforceRangeU32(cx, initialVal, noun, "initial size", &initial)) {
|
||||
return false;
|
||||
}
|
||||
limits->initial = initial;
|
||||
|
||||
if (limits->initial > maximumField) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
|
||||
kind, "initial size");
|
||||
noun, "initial size");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -902,7 +904,7 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
|
|||
|
||||
uint32_t minimum = 0;
|
||||
if (!minimumVal.isUndefined() &&
|
||||
!EnforceRangeU32(cx, minimumVal, kind, "initial size", &minimum)) {
|
||||
!EnforceRangeU32(cx, minimumVal, noun, "initial size", &minimum)) {
|
||||
return false;
|
||||
}
|
||||
if (!minimumVal.isUndefined()) {
|
||||
|
@ -925,21 +927,24 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
|
|||
// maxVal does not have a default value.
|
||||
if (!maxVal.isUndefined()) {
|
||||
uint32_t maximum = 0;
|
||||
if (!EnforceRangeU32(cx, maxVal, kind, "maximum size", &maximum)) {
|
||||
if (!EnforceRangeU32(cx, maxVal, noun, "maximum size", &maximum)) {
|
||||
return false;
|
||||
}
|
||||
limits->maximum = Some(maximum);
|
||||
|
||||
if (*limits->maximum > maximumField || limits->initial > *limits->maximum) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_BAD_RANGE, kind, "maximum size");
|
||||
JSMSG_WASM_BAD_RANGE, noun, "maximum size");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
limits->indexType = IndexType::I32;
|
||||
limits->shared = Shareable::False;
|
||||
|
||||
if (allowShared == Shareable::True) {
|
||||
// Memory limits may be shared or specify an alternate index type
|
||||
if (kind == LimitsKind::Memory) {
|
||||
// Get the shared field
|
||||
JSAtom* sharedAtom = Atomize(cx, "shared", strlen("shared"));
|
||||
if (!sharedAtom) {
|
||||
return false;
|
||||
|
@ -959,7 +964,7 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
|
|||
if (limits->shared == Shareable::True) {
|
||||
if (maxVal.isUndefined()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_MISSING_MAXIMUM, kind);
|
||||
JSMSG_WASM_MISSING_MAXIMUM, noun);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -972,6 +977,33 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WASM_MEMORY64
|
||||
// Get the index type field
|
||||
JSAtom* indexTypeAtom = Atomize(cx, "index", strlen("index"));
|
||||
if (!indexTypeAtom) {
|
||||
return false;
|
||||
}
|
||||
RootedId indexTypeId(cx, AtomToId(indexTypeAtom));
|
||||
|
||||
RootedValue indexTypeVal(cx);
|
||||
if (!GetProperty(cx, obj, obj, indexTypeId, &indexTypeVal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The index type has a default value
|
||||
if (!indexTypeVal.isUndefined()) {
|
||||
if (!ToIndexType(cx, indexTypeVal, &limits->indexType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (limits->indexType == IndexType::I64 && !Memory64Available(cx)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_NO_MEM64_LINK);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WASM_TYPE_REFLECTIONS
|
||||
|
@ -2337,6 +2369,7 @@ WasmMemoryObject* WasmMemoryObject::create(
|
|||
|
||||
obj->initReservedSlot(BUFFER_SLOT, ObjectValue(*buffer));
|
||||
MOZ_ASSERT(!obj->hasObservers());
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -2360,8 +2393,7 @@ bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) {
|
|||
|
||||
RootedObject obj(cx, &args[0].toObject());
|
||||
Limits limits;
|
||||
if (!GetLimits(cx, obj, MaxMemory32LimitField, "Memory", &limits,
|
||||
Shareable::True)) {
|
||||
if (!GetLimits(cx, obj, MaxMemory32LimitField, LimitsKind::Memory, &limits)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2370,7 +2402,7 @@ bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) {
|
|||
JSMSG_WASM_MEM_IMP_LIMIT);
|
||||
return false;
|
||||
}
|
||||
MemoryDesc memory(MemoryKind::Memory32, limits);
|
||||
MemoryDesc memory(limits);
|
||||
|
||||
RootedArrayBufferObjectMaybeShared buffer(cx);
|
||||
if (!CreateWasmBuffer32(cx, memory, &buffer)) {
|
||||
|
@ -2558,6 +2590,13 @@ Maybe<wasm::Pages> WasmMemoryObject::maxPages() const {
|
|||
return buffer().wasmMaxPages();
|
||||
}
|
||||
|
||||
wasm::IndexType WasmMemoryObject::indexType() const {
|
||||
if (isShared()) {
|
||||
return sharedArrayRawBuffer()->wasmIndexType();
|
||||
}
|
||||
return buffer().wasmIndexType();
|
||||
}
|
||||
|
||||
bool WasmMemoryObject::isShared() const {
|
||||
return buffer().is<SharedArrayBufferObject>();
|
||||
}
|
||||
|
@ -2910,11 +2949,13 @@ bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) {
|
|||
}
|
||||
|
||||
Limits limits;
|
||||
if (!GetLimits(cx, obj, MaxTableLimitField, "Table", &limits,
|
||||
Shareable::False)) {
|
||||
if (!GetLimits(cx, obj, MaxTableLimitField, LimitsKind::Table, &limits)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Converting limits for a table only supports i32
|
||||
MOZ_ASSERT(limits.indexType == IndexType::I32);
|
||||
|
||||
if (limits.initial > MaxTableLength) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_TABLE_IMP_LIMIT);
|
||||
|
|
|
@ -416,6 +416,7 @@ class WasmMemoryObject : public NativeObject {
|
|||
// contrast to the current length, as it cannot change for shared memories.
|
||||
mozilla::Maybe<wasm::Pages> maxPages() const;
|
||||
|
||||
wasm::IndexType indexType() const;
|
||||
bool isShared() const;
|
||||
bool isHuge() const;
|
||||
bool movingGrowable() const;
|
||||
|
|
|
@ -705,12 +705,17 @@ bool Module::instantiateMemory(JSContext* cx,
|
|||
}
|
||||
|
||||
MemoryDesc desc = *metadata().memory;
|
||||
MOZ_ASSERT(desc.kind == MemoryKind::Memory32);
|
||||
|
||||
if (memory) {
|
||||
MOZ_ASSERT_IF(metadata().isAsmJS(), memory->buffer().isPreparedForAsmJS());
|
||||
MOZ_ASSERT_IF(!metadata().isAsmJS(), memory->buffer().isWasm());
|
||||
|
||||
if (memory->indexType() != desc.indexType()) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_BAD_IMP_INDEX,
|
||||
ToString(memory->indexType()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CheckLimits(cx, desc.initialPages(), desc.maximumPages(),
|
||||
/* defaultMax */ MaxMemory32Pages(),
|
||||
/* actualLength */
|
||||
|
|
|
@ -1882,7 +1882,7 @@ inline bool OpIter<Policy>::readMemorySize() {
|
|||
return fail("failed to read memory flags");
|
||||
}
|
||||
|
||||
if (flags != uint8_t(MemoryTableFlags::Default)) {
|
||||
if (flags != uint8_t(0)) {
|
||||
return fail("unexpected flags");
|
||||
}
|
||||
|
||||
|
@ -1902,7 +1902,7 @@ inline bool OpIter<Policy>::readMemoryGrow(Value* input) {
|
|||
return fail("failed to read memory flags");
|
||||
}
|
||||
|
||||
if (flags != uint8_t(MemoryTableFlags::Default)) {
|
||||
if (flags != uint8_t(0)) {
|
||||
return fail("unexpected flags");
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,16 @@
|
|||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
// Limits are parameterized by an IndexType which is used to index the
|
||||
// underlying resource (either a Memory or a Table). Tables are restricted to
|
||||
// I32, while memories may use I64 when memory64 is enabled.
|
||||
|
||||
enum class IndexType : uint8_t { I32, I64 };
|
||||
|
||||
extern bool ToIndexType(JSContext* cx, HandleValue value, IndexType* indexType);
|
||||
|
||||
extern const char* ToString(IndexType indexType);
|
||||
|
||||
// Pages is a typed unit representing a multiple of wasm::PageSize. We
|
||||
// generally use pages as the unit of length when representing linear memory
|
||||
// lengths so as to avoid overflow when the specified initial or maximum pages
|
||||
|
|
|
@ -553,6 +553,40 @@ const CodeRange* wasm::LookupInSorted(const CodeRangeVector& codeRanges,
|
|||
return &codeRanges[match];
|
||||
}
|
||||
|
||||
const char* wasm::ToString(IndexType indexType) {
|
||||
switch (indexType) {
|
||||
case IndexType::I32:
|
||||
return "i32";
|
||||
case IndexType::I64:
|
||||
return "i64";
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
bool wasm::ToIndexType(JSContext* cx, HandleValue value, IndexType* indexType) {
|
||||
RootedString typeStr(cx, ToString(cx, value));
|
||||
if (!typeStr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedLinearString typeLinearStr(cx, typeStr->ensureLinear(cx));
|
||||
if (!typeLinearStr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringEqualsLiteral(typeLinearStr, "i32")) {
|
||||
*indexType = IndexType::I32;
|
||||
} else if (StringEqualsLiteral(typeLinearStr, "i64")) {
|
||||
*indexType = IndexType::I64;
|
||||
} else {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_BAD_STRING_IDX_TYPE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void wasm::Log(JSContext* cx, const char* fmt, ...) {
|
||||
MOZ_ASSERT(!cx->isExceptionPending());
|
||||
|
||||
|
|
|
@ -1250,9 +1250,22 @@ struct WasmTryNote {
|
|||
|
||||
WASM_DECLARE_POD_VECTOR(WasmTryNote, WasmTryNoteVector)
|
||||
|
||||
// The kind of limits to decode or convert from JS.
|
||||
|
||||
enum class LimitsKind {
|
||||
Memory,
|
||||
Table,
|
||||
};
|
||||
|
||||
// Represents the resizable limits of memories and tables.
|
||||
|
||||
struct Limits {
|
||||
// `indexType` will always be I32 for tables, but may be I64 for memories
|
||||
// when memory64 is enabled.
|
||||
IndexType indexType;
|
||||
|
||||
// The initial and maximum limit. The unit is pages for memories and elements
|
||||
// for tables.
|
||||
uint64_t initial;
|
||||
Maybe<uint64_t> maximum;
|
||||
|
||||
|
@ -1266,15 +1279,9 @@ struct Limits {
|
|||
: initial(initial), maximum(maximum), shared(shared) {}
|
||||
};
|
||||
|
||||
// Memories can be 32-bit (indices are 32 bits and the max is 4GB) or 64-bit
|
||||
// (indices are 64 bits and the max is XXX).
|
||||
|
||||
enum class MemoryKind { Memory32, Memory64 };
|
||||
|
||||
// MemoryDesc describes a memory.
|
||||
|
||||
struct MemoryDesc {
|
||||
MemoryKind kind;
|
||||
Limits limits;
|
||||
|
||||
bool isShared() const { return limits.shared == Shareable::True; }
|
||||
|
@ -1290,6 +1297,8 @@ struct MemoryDesc {
|
|||
limits.maximum.value() < (0x100000000 / PageSize);
|
||||
}
|
||||
|
||||
IndexType indexType() const { return limits.indexType; }
|
||||
|
||||
// The initial length of this memory in pages.
|
||||
Pages initialPages() const { return Pages(limits.initial); }
|
||||
|
||||
|
@ -1300,13 +1309,13 @@ struct MemoryDesc {
|
|||
|
||||
// The initial length of this memory in bytes. Only valid for memory32.
|
||||
uint64_t initialLength32() const {
|
||||
MOZ_ASSERT(kind == MemoryKind::Memory32);
|
||||
MOZ_ASSERT(indexType() == IndexType::I32);
|
||||
// See static_assert after MemoryDesc for why this is safe.
|
||||
return limits.initial * PageSize;
|
||||
}
|
||||
|
||||
MemoryDesc() = default;
|
||||
explicit MemoryDesc(MemoryKind kind, Limits limits) : kind(kind), limits(limits) {}
|
||||
explicit MemoryDesc(Limits limits) : limits(limits) {}
|
||||
};
|
||||
|
||||
// We don't need to worry about overflow with a Memory32 field when
|
||||
|
|
|
@ -1691,16 +1691,14 @@ static bool DecodeFuncTypeIndex(Decoder& d, const TypeContext& types,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool DecodeLimits(Decoder& d, Limits* limits,
|
||||
Shareable allowShared = Shareable::False) {
|
||||
static bool DecodeLimits(Decoder& d, LimitsKind kind, Limits* limits) {
|
||||
uint8_t flags;
|
||||
if (!d.readFixedU8(&flags)) {
|
||||
return d.fail("expected flags");
|
||||
}
|
||||
|
||||
uint8_t mask = allowShared == Shareable::True
|
||||
? uint8_t(MemoryMasks::AllowShared)
|
||||
: uint8_t(MemoryMasks::AllowUnshared);
|
||||
uint8_t mask = kind == LimitsKind::Memory ? uint8_t(LimitsMask::Memory)
|
||||
: uint8_t(LimitsMask::Table);
|
||||
|
||||
if (flags & ~uint8_t(mask)) {
|
||||
return d.failf("unexpected bits set in flags: %" PRIu32,
|
||||
|
@ -1713,7 +1711,7 @@ static bool DecodeLimits(Decoder& d, Limits* limits,
|
|||
}
|
||||
limits->initial = initial;
|
||||
|
||||
if (flags & uint8_t(MemoryTableFlags::HasMaximum)) {
|
||||
if (flags & uint8_t(LimitsFlags::HasMaximum)) {
|
||||
uint32_t maximum;
|
||||
if (!d.readVarU32(&maximum)) {
|
||||
return d.fail("expected maximum length");
|
||||
|
@ -1730,16 +1728,27 @@ static bool DecodeLimits(Decoder& d, Limits* limits,
|
|||
}
|
||||
|
||||
limits->shared = Shareable::False;
|
||||
limits->indexType = IndexType::I32;
|
||||
|
||||
if (allowShared == Shareable::True) {
|
||||
if ((flags & uint8_t(MemoryTableFlags::IsShared)) &&
|
||||
!(flags & uint8_t(MemoryTableFlags::HasMaximum))) {
|
||||
// Memory limits may be shared or specify an alternate index type
|
||||
if (kind == LimitsKind::Memory) {
|
||||
if ((flags & uint8_t(LimitsFlags::IsShared)) &&
|
||||
!(flags & uint8_t(LimitsFlags::HasMaximum))) {
|
||||
return d.fail("maximum length required for shared memory");
|
||||
}
|
||||
|
||||
limits->shared = (flags & uint8_t(MemoryTableFlags::IsShared))
|
||||
limits->shared = (flags & uint8_t(LimitsFlags::IsShared))
|
||||
? Shareable::True
|
||||
: Shareable::False;
|
||||
|
||||
#ifdef ENABLE_WASM_MEMORY64
|
||||
limits->indexType =
|
||||
(flags & uint8_t(LimitsFlags::IsI64)) ? IndexType::I64 : IndexType::I32;
|
||||
#else
|
||||
if (flags & uint8_t(LimitsFlags::IsI64)) {
|
||||
return d.fail("i64 is not supported for memory limits");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1757,10 +1766,13 @@ static bool DecodeTableTypeAndLimits(Decoder& d, const FeatureArgs& features,
|
|||
}
|
||||
|
||||
Limits limits;
|
||||
if (!DecodeLimits(d, &limits)) {
|
||||
if (!DecodeLimits(d, LimitsKind::Table, &limits)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decoding limits for a table only supports i32
|
||||
MOZ_ASSERT(limits.indexType == IndexType::I32);
|
||||
|
||||
// If there's a maximum, check it is in range. The check to exclude
|
||||
// initial > maximum is carried out by the DecodeLimits call above, so
|
||||
// we don't repeat it here.
|
||||
|
@ -1841,13 +1853,13 @@ static bool DecodeGlobalType(Decoder& d, const TypeContext& types,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool DecodeMemoryLimits(Decoder& d, ModuleEnvironment* env) {
|
||||
static bool DecodeMemoryTypeAndLimits(Decoder& d, ModuleEnvironment* env) {
|
||||
if (env->usesMemory()) {
|
||||
return d.fail("already have default memory");
|
||||
}
|
||||
|
||||
Limits limits;
|
||||
if (!DecodeLimits(d, &limits, Shareable::True)) {
|
||||
if (!DecodeLimits(d, LimitsKind::Memory, &limits)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1864,7 +1876,11 @@ static bool DecodeMemoryLimits(Decoder& d, ModuleEnvironment* env) {
|
|||
return d.fail("shared memory is disabled");
|
||||
}
|
||||
|
||||
env->memory = Some(MemoryDesc(MemoryKind::Memory32, limits));
|
||||
if (limits.indexType == IndexType::I64 && !env->memory64Enabled()) {
|
||||
return d.fail("memory64 is disabled");
|
||||
}
|
||||
|
||||
env->memory = Some(MemoryDesc(limits));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1987,7 +2003,7 @@ static bool DecodeImport(Decoder& d, ModuleEnvironment* env,
|
|||
break;
|
||||
}
|
||||
case DefinitionKind::Memory: {
|
||||
if (!DecodeMemoryLimits(d, env)) {
|
||||
if (!DecodeMemoryTypeAndLimits(d, env)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
@ -2160,7 +2176,7 @@ static bool DecodeMemorySection(Decoder& d, ModuleEnvironment* env) {
|
|||
}
|
||||
|
||||
for (uint32_t i = 0; i < numMemories; ++i) {
|
||||
if (!DecodeMemoryLimits(d, env)) {
|
||||
if (!DecodeMemoryTypeAndLimits(d, env)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче