Skip to content
This repository has been archived by the owner on Jan 15, 2025. It is now read-only.

Adjust js-api spec's table implementation-defined limits #103

Closed
wants to merge 2 commits into from

Conversation

evicy
Copy link
Contributor

@evicy evicy commented Nov 13, 2024

No description provided.

@bvisness
Copy link
Collaborator

bvisness commented Nov 13, 2024

I don't think we want to do this. The intent of adding table64 was to enable i64 function pointers, not to allow dramatically larger tables. In particular, in SpiderMonkey we have not relaxed our implementation limit on the actual size of tables, nor do we actually allow i64 tables larger than 2^32 items, as all tables are subject to the same runtime limit: https://searchfox.org/mozilla-central/source/js/src/wasm/WasmConstants.h#1150

It's important to recognize that this implementation-defined limit in the JS API has nothing to do with validation, even when constructing new tables via the JS API.

@@ -1780,7 +1778,7 @@ An implementation must throw a {{RuntimeError}} if one of the following limits i
In practice, an implementation may run out of resources for valid modules below these limits.

<ul>
<li>The maximum size of a table is 10,000,000.</li>
<li>The maximal initial or maximum size of a table is 10,000,000.</li>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. This is a runtime condition; it's about the actual size of the table, not the limits.

@backes
Copy link
Member

backes commented Nov 14, 2024

I think the whole section is a bit unclear and needs clarification. Thanks for working on this, Eva!

In the first section about CompileErrors we probably want to say that the maximum initial size is 10M. Otherwise the module is invalid.
And I think we can keep the sentence about initializers.

In the second section we mean that there is a dynamic limit of 10M; no table can ever grow bigger than that. This means that we return -1 when trying to grow in Wasm and we throw a RangeError if growing via JS, right? Not sure why it's talking about throwing a RuntimeError.

The declared maximum is not restricted by any of this.

@bvisness
Copy link
Collaborator

bvisness commented Nov 14, 2024

Our implementation throws a RuntimeError when constructing a Table through new WebAssembly.Table(). It doesn't make sense for it to be a CompileError, after all, and since it's not a Table.grow it doesn't trigger the RangeError.

I do think the spec is confusing about this as it currently stands. I think the cases we need the JS API spec to cover, and be clearer about, are:

  • Hitting implementation limits when compiling a WebAssembly module
  • Hitting implementation limits when creating WebAssembly objects like memories and tables directly
  • Hitting implementation limits at runtime (usually?) (only?) through grows.

@backes
Copy link
Member

backes commented Nov 21, 2024

Eva uploaded a new commit; we tried to make the wording more clear and removed the mention of a RuntimeError.
@bvisness can you check if this matches your understanding?

@backes
Copy link
Member

backes commented Dec 17, 2024

@bvisness What's your opinion on this?

If we merge this, then this test will need to be removed again: https://github.com/WebAssembly/memory64/blob/main/test/core/memory64.wast#L8
It will fail in web embeddings otherwise.

Copy link
Collaborator

@bvisness bvisness left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally I think this is good now. Restricting the runtime limits to only apply to grows makes sense and defers the type of error to the actual grow method. I just have some smaller comments below.

@@ -1747,15 +1747,15 @@ Note: ECMAScript doesn't specify any sort of behavior on out-of-memory condition

The WebAssembly core specification allows an implementation to define limits on the syntactic structure of the module.
While each embedding of WebAssembly may choose to define its own limits, for predictability the standard WebAssembly JavaScript Interface described in this document defines the following exact limits.
An implementation must reject a module that exceeds one of the following limits with a {{CompileError}}.
An implementation must reject a module that exceeds one of the following limits with a {{CompileError}}, i.e. the module is considered invalid.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, but I don't think the module is really "considered invalid" in this situation. If you want to clarify the symmetry with the behavior for invalid modules, I would maybe say "as if the module was invalid".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, doesn't throwing CompileError indicate that the module is considered invalid? WebAssembly.validate would also return false if we exceed these limits, no?

The spec uses the module_validate condition to decide whether to throw an error or to return the compiled module (and the same condition is used for WebAssembly.validate).
module_validate again uses the terminology "if module is valid, ...".

So either the implementation-defined limits make modules invalid, or we need to extend the module_validate step to say something like "if module is valid and does not exceed implementation defined limits, ...".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, it seems there's actually quite a large discrepancy between engines on this point. I tested the module (module (table 1000000000 funcref)) and got the following:

$ js test.js
Is module valid? true
Does module compile? true
Does module instantiate? false
RuntimeError: too many table elements
$ d8 test.js
Is module valid? false
Does module compile? false
CompileError: WebAssembly.Module(): initial table size (1000000000 elements) is larger than implementation limit (10000000 elements) @+13

The fact that the spec demands a CompileError does seem to imply that we should at least be rejecting this module in new WebAssembly.Module and similar compilation paths. But, I find it very unpleasant that a perfectly valid wasm module would be rejected by WebAssembly.validate, since I'd expect WebAssembly.validate to be a thin proxy for module_validate (which would not return error).

Really the latter is the key point for me though: a module that exceeds the JS API implementation limits is still valid per the core wasm spec, and module_validate therefore wouldn't return error. Hence this confusion, I guess.

I think all this is outside the scope of this PR. I retract this complaint for now but I think I'll open another issue to discuss this confusion.

<li>The maximum number of table entries in any table initialization is 10,000,000.</li>
<li>The maximum initial size of a 32-bit memory is 65,536 pages (4 GiB).</li>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is impossible to request a 32-bit memory larger than 4GB, so this limit doesn't really mean anything. It should probably be removed.

<li>The maximum size of a 32-bit memory is 65,536 pages (4 GiB).</li>
<li>The maximum size of a 64-bit memory is 262,144 pages (16 GiB).</li>
<li>A table can grow to have up to 10,000,000 entries.</li>
<li>A 32-bit memory can grow to have up to 65,536 pages (4 GiB).</li>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again this limit doesn't mean anything; you can't have a 32-bit memory larger than 4GB. (I realize this one was already present, but I think it should be removed.)

@bvisness
Copy link
Collaborator

bvisness commented Jan 13, 2025

I just realized that this is still on the memory64 repo. This should probably be migrated to the core spec repo if we still want to address it in this form. (Specifically targeting the wasm-3.0 branch.)

@backes
Copy link
Member

backes commented Jan 14, 2025

Some changes touch lines that were only added in this repo, so part of it will have to stay in this repo.

But I think it makes sense to first resolve WebAssembly/spec#1863 in the wasm-3.0 branch (thanks for opening that issue!), then rebase this repo and then potentially adjust the memory64-specific lines.

@sbc100
Copy link
Member

sbc100 commented Jan 14, 2025

Some changes touch lines that were only added in this repo, so part of it will have to stay in this repo.

But I think it makes sense to first resolve WebAssembly/spec#1863 in the wasm-3.0 branch (thanks for opening that issue!), then rebase this repo and then potentially adjust the memory64-specific lines.

Should we still be making changes in this repo now that its been merged into the upstream wasm-3.0 branch? I would have thought maybe we should archive/close this repo and any followups should happen directly upstream? Or can we continue to fix stuff downstream for some amount of time?

@backes
Copy link
Member

backes commented Jan 14, 2025

Some changes touch lines that were only added in this repo, so part of it will have to stay in this repo.
But I think it makes sense to first resolve WebAssembly/spec#1863 in the wasm-3.0 branch (thanks for opening that issue!), then rebase this repo and then potentially adjust the memory64-specific lines.

Should we still be making changes in this repo now that its been merged into the upstream wasm-3.0 branch? I would have thought maybe we should archive/close this repo and any followups should happen directly upstream? Or can we continue to fix stuff downstream for some amount of time?

Oh, sorry, I didn't realize that this repo is already merged into wasm-3.0. In that case we should do all changes there of course.

@sbc100
Copy link
Member

sbc100 commented Jan 14, 2025

@rossberg should we archive this repo at this point? (Or am I misunderstanding and can/should changes still be possible downstream?)

@rossberg
Copy link
Member

I'm fine either way. It simplifies my life a bit if you do it on the wasm-3.0 branch, especially if there is potential for conflicts. But given that other repos also still have occasional activity, I have gone into the habit of doing regular upstreaming rounds. Though I would appreciate it if champions also created PRs for upstreaming when they do changes in other repos.

@sbc100
Copy link
Member

sbc100 commented Jan 14, 2025

If it were up to me, my preference would be to archive this repo and have any followups happen upstream. @evicy, @bvisness @backes if any of you disagree, and would rather continue work here, I don't feel strongly about it.

@sbc100
Copy link
Member

sbc100 commented Jan 15, 2025

Closing this PR since this proposal is done and the repo is being archived. Perhaps re-open it against wasm-3.0 in the spec repo.

@sbc100 sbc100 closed this Jan 15, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants