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:
Ryan Hunt 2021-07-16 20:58:09 +00:00
Родитель f095d458ea
Коммит 40572aafa3
20 изменённых файлов: 305 добавлений и 2132 удалений

Просмотреть файл

@ -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;
}
}