spec
spec copied to clipboard
What to do about maxByteLength for resizable buffers of WebAssembly.Memorys?
Problem
WebAssembly.Memory instances are getting a toResizableBuffer() method per #1292.
Resizable ArrayBuffers have a maxByteLength property. For 32bit memories, the maximum spec byte size of a WebAssembly.Memory is 65536 pages, or 2^32 bytes. For 64bit memories, the the maximum size can easily be > 65535 pages.
The problem is any max sizes > 65535 pages are unrepresentable in 32bits as bytes. V8 and SpiderMonkey use size_t to represent lengths for ArrayBuffers, and on 32bits, size_t is only 32bits.
For 32bit memories, the only unrepresentable value is the spec maximum of 65536 pages. For 64bit memories the problem is worse, as the whole point is to have larger memories.
Other interesting properties you might care about:
WebAssembly.Memorydoes not require a maximum for unshared memories;ArrayBuffer()always require a maximumWebAssembly.Memory's maximum is not inspectable by user code;ArrayBuffers' maximum is inspectable
Possible solutions
Rejected proposals
1a. Clamp maxByteLength to a single, arch-independent value
If mem.toResizableBuffer() is called, clamp the returned ArrayBuffer's maxByteLength to some single, arch-independent value, like 65535 * 65536.
Pros
- Architecture independent
Cons
- Significantly reduces utility for 64bit memories on 64bit architectures
- Mismatch with JS API where the maximum passed to the
WebAssembly.Memoryconstructor differs from themaxByteLengthproperty.
1b. Clamp maxByteLength on 32bit architectures only
If mem.toResizableBuffer() is called, clamp the returned ArrayBuffer's maxByteLength to 65535 * 65536 only on 32bit architectures.
Pros
- Minimal change
Cons
- Architecture dependent: usage of 64bit memories need to be aware if it's running on 32bit or 64bit machines. FWIW this awareness is already required for pure JS usage of resizable buffers.
- Mismatch with JS API where the maximum passed to the
WebAssembly.Memoryconstructor differs from themaxByteLengthproperty.
2a. Throw in toResizableBuffer() if maxByteLength exceeds a single, arch-independent limit
If mem.toResizableBuffer() is called, and mem's maximum byte size exceeds some single, arch-independent value, like 65535 * 65536, then throw a RangeError.
Pros
- Sidesteps the question of matching the JS API
Cons
- Significantly reduces utility for 64bit memories on 64bit architectures
2b. Throw in toResizableBuffer() if maxByteLength exceeds 2^32 on 32bit architectures only
If mem.toResizableBuffer() is called on 32bit architectures, and mem's maximum byte size exceeds 65535 * 65536, then throw a RangeError.
Pros
- Sidesteps the question of matching the JS API
Cons
- Architecture dependent
3. Change spec to throw early when constructing WebAssembly.Memorys with unsatisfable maximum sizes
If a maximum is passed to WebAssembly.Memory that is always unsatisfiable, e.g. > 65536 pages on 32bit architectures, throw a RangeError. For backwards compatibility with existing code, 65536 still needs to be accepted.
Cons
- Architecture dependent
- Against the spirit of the Wasm design of max size (?)
4. Change implementations to accommodate size values > 2^32 - 1
Pros
- Transparent
Cons
- Complexity in runtime implementations, as lengths flow into many arithmetic operations, with possible knock-on effects (e.g. on 32bit architectures, optimizing tiers can no longer assume byte lengths from TypedArrays and ArrayBuffers are always 32bits)
5. Have the resizable buffer report an engine-determined maxByteLength
(suggested by @lukewagner)
If mem.toResizableBuffer() is called, the returned ArrayBuffer can have an implementation-defined maxByteLength that is smaller than the requested max size passed to the WebAssembly.Memory constructor. This aligns more closely with Wasm memories' concept of max as a hint that the engine can decrease as it probes.
Pros
- Trivial implementation
Cons
- Nondeterminism
- Mismatch with pure JS usage of RABs. But there is an argument to be made that the interpretation of maximum is just different between pure JS uses of RABs and Wasm memories. Pure JS uses of RABs are varied, while the "have the engine probe for an actual maximum" heuristic makes sense if you're allocating a Wasm program's heap memory and you want as much as you can get.
- Doesn't compose as nicely with the decision made in #1871. I think if the CG chooses this option, we should reverse the decision in #1871.
6. Have maxByteLength be Infinity for Wasm memories
(suggested by @eqrion)
If mem.toResizableBuffer() is called, the returned ArrayBuffer will have Infinity as the value for maxByteLength. Also reverse the decision in #1871.
Open question on what mem.type() would return.
Pros
- Sidesteps the representation issues
- Deterministic
- Captures the difference in interpretation of "max" between Wasm memories and JS uses of resizable ABs
Cons
- In some engines (V8), resizable ABs still will have the implementation constraint of needing to always grow in place.
Infinitymay give the wrong intuition.
7. "Lie" in the maxByteLength getter
(after discussion with @eqrion)
See https://github.com/WebAssembly/spec/issues/1895#issuecomment-2895078022 for details.
Pros
- No representation issue for the actual max
- Deterministic