diff --git a/.github/workflows/ci-spec.yml b/.github/workflows/ci-spec.yml index 75a88f58c..c35b394f1 100644 --- a/.github/workflows/ci-spec.yml +++ b/.github/workflows/ci-spec.yml @@ -35,7 +35,7 @@ jobs: - name: Run Bikeshed run: cd document/core && make bikeshed - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: core-rendered path: document/core/_build/html @@ -50,7 +50,7 @@ jobs: - name: Run Bikeshed run: bikeshed spec "document/js-api/index.bs" "document/js-api/index.html" - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: js-api-rendered path: document/js-api/index.html @@ -65,7 +65,7 @@ jobs: - name: Run Bikeshed run: bikeshed spec "document/web-api/index.bs" "document/web-api/index.html" - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: web-api-rendered path: document/web-api/index.html @@ -85,7 +85,7 @@ jobs: - name: Build main spec run: cd document/metadata/code && make main - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: code-metadata-rendered path: document/metadata/code/_build/html @@ -104,7 +104,7 @@ jobs: - name: Build main spec run: cd document/legacy/exceptions/core && make main - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: legacy-exceptions-core-rendered path: document/legacy/exceptions/core/_build/html @@ -119,49 +119,72 @@ jobs: - name: Run Bikeshed run: bikeshed spec "document/legacy/exceptions/js-api/index.bs" "document/legacy/exceptions/js-api/index.html" - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: legacy-exceptions-js-api-rendered path: document/legacy/exceptions/js-api/index.html + build-spec-versions: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: versions-rendered + path: document/versions/ + publish-spec: runs-on: ubuntu-latest - needs: [build-core-spec, build-js-api-spec, build-web-api-spec, build-code-metadata-spec, build-legacy-exceptions-core-spec, build-legacy-exceptions-js-api-spec] + needs: + - build-core-spec + - build-js-api-spec + - build-web-api-spec + - build-code-metadata-spec + - build-legacy-exceptions-core-spec + - build-legacy-exceptions-js-api-spec + - build-spec-versions steps: - name: Checkout repo uses: actions/checkout@v2 - name: Create output directory run: mkdir _output && cp document/index.html _output/index.html - name: Download core spec artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: core-rendered path: _output/core - name: Download JS API spec artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: js-api-rendered path: _output/js-api - name: Download Web API spec artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: web-api-rendered path: _output/web-api - name: Download code metadata spec artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: code-metadata-rendered path: _output/metadata/code - name: Download legacy exceptions core spec artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: legacy-exceptions-core-rendered path: _output/legacy/exceptions/core - name: Download legacy exceptions JS API spec artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: legacy-exceptions-js-api-rendered path: _output/legacy/exceptions/js-api + - name: Download spec versions artifacts + uses: actions/download-artifact@v4 + with: + name: versions-rendered + path: _output/versions - name: Publish to GitHub Pages if: github.ref == 'refs/heads/main' uses: peaceiris/actions-gh-pages@v3 diff --git a/.github/workflows/w3c-publish.yml b/.github/workflows/w3c-publish.yml index d1b267592..cd43f17a3 100644 --- a/.github/workflows/w3c-publish.yml +++ b/.github/workflows/w3c-publish.yml @@ -5,12 +5,23 @@ on: branches: [ main ] paths: [ .github/**, document/** ] - # Allows you to run this workflow manually from the Actions tab + # Allows you to run this workflow manually from the Actions tab, gh CLI tool, + # or REST API. THe w3c-status options correspond to the valid options for + # Bikeshed's --md-status flag, and refer to the W3C rec-track document + # stages described in https://www.w3.org/policies/process/#maturity-stages + # (Editor's Draft, Working Draft, Candidiate Recommendation Draft, and + # Candidate Recommendation Snapshot). workflow_dispatch: inputs: w3c-status: required: true - type: string + type: choice + description: W3C Document Status + options: + - ED + - WD + - CRD + - CR jobs: publish-to-w3c-TR: @@ -34,7 +45,7 @@ jobs: - name: Publish all specs to their https://www.w3.org/TR/ URLs run: cd document && make -e WD-echidna-CI env: - W3C_STATUS: ${{ github.event_name == 'push' && 'WD' || inputs.w3c-status }} + W3C_STATUS: ${{ github.event_name == 'push' && 'CRD' || inputs.w3c-status }} W3C_ECHIDNA_TOKEN_CORE: ${{ secrets.W3C_ECHIDNA_TOKEN_CORE }} W3C_ECHIDNA_TOKEN_JSAPI: ${{ secrets.W3C_ECHIDNA_TOKEN_JSAPI }} W3C_ECHIDNA_TOKEN_WEBAPI: ${{ secrets.W3C_ECHIDNA_TOKEN_WEBAPI }} diff --git a/document/Makefile b/document/Makefile index e5b35d6c7..77cc20a34 100644 --- a/document/Makefile +++ b/document/Makefile @@ -1,4 +1,4 @@ -DIRS = core js-api web-api metadata/code legacy/exceptions +DIRS = core js-api web-api metadata/code legacy/exceptions versions FILES = index.html BUILDDIR = _build TAR = tar @@ -60,7 +60,7 @@ $(DIRS:%=build-%): build-%: (cd $(@:build-%=%); make BUILDDIR=$(BUILDDIR) all) .PHONY: $(DIRS:%=dir-%) -$(DIRS:%=dir-%): dir-%: +$(DIRS:%=dir-%): dir-%: $(BUILDDIR) mkdir -p $(BUILDDIR)/$(@:dir-%=%) rm -rf $(BUILDDIR)/$(@:dir-%=%)/* cp -R $(@:dir-%=%)/$(BUILDDIR)/html/* $(BUILDDIR)/$(@:dir-%=%)/ diff --git a/document/core/Makefile b/document/core/Makefile index 2ca6e828a..df245c881 100644 --- a/document/core/Makefile +++ b/document/core/Makefile @@ -12,6 +12,7 @@ DOWNLOADDIR = _download NAME = WebAssembly DECISION_URL = https://github.com/WebAssembly/meetings/blob/main/main/2024/WG-06-12.md TAR = tar +DEADLINE = $(shell date -d "+30 days" +%Y-%m-%d 2>/dev/null || date -v +30d +%Y-%m-%d) # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 @@ -155,7 +156,7 @@ bikeshed: $(GENERATED) @echo @echo ========================================================================= mkdir -p $(BUILDDIR)/bikeshed_mathjax/ - bikeshed spec --md-status=$(W3C_STATUS) index.bs $(BUILDDIR)/bikeshed_mathjax/index.html + bikeshed spec --md-status=$(W3C_STATUS) --md-deadline=$(DEADLINE) index.bs $(BUILDDIR)/bikeshed_mathjax/index.html mkdir -p $(BUILDDIR)/html/bikeshed/ (cd util/katex/ && yarn && yarn build && npm install --only=prod) python3 util/mathjax2katex.py $(BUILDDIR)/bikeshed_mathjax/index.html \ diff --git a/document/core/appendix/changes.rst b/document/core/appendix/changes.rst index 7c2386081..39f40f9cf 100644 --- a/document/core/appendix/changes.rst +++ b/document/core/appendix/changes.rst @@ -17,7 +17,9 @@ Sign Extension Instructions Added new numeric instructions for performing sign extension within integer representations. [#proposal-signext]_ -* New :ref:`numeric instructions `: :math:`\K{i}\X{nn}\K{.}\EXTEND\X{N}\K{\_s}` +* New :ref:`numeric instructions `: + + - :math:`\K{i}\X{nn}\K{.}\EXTEND\X{N}\K{\_s}` .. index:: instruction, trap, floating-point, integer @@ -27,7 +29,9 @@ Non-trapping Float-to-Int Conversions Added new conversion instructions that avoid trapping when converting a floating-point number to an integer. [#proposal-cvtsat]_ -* New :ref:`numeric instructions `: :math:`\K{i}\X{nn}\K{.}\TRUNC\K{\_sat\_f}\X{mm}\K{\_}\sx` +* New :ref:`numeric instructions `: + + - :math:`\K{i}\X{nn}\K{.}\TRUNC\K{\_sat\_f}\X{mm}\K{\_}\sx` .. index:: block, function, value type, result type @@ -49,11 +53,20 @@ Reference Types Added |FUNCREF| and |EXTERNREF| as new value types and respective instructions. [#proposal-reftype]_ -* New :ref:`value types `: :ref:`reference types ` |FUNCREF| and |EXTERNREF| +* New :ref:`reference ` :ref:`value types `: + + - |FUNCREF| + - |EXTERNREF| -* New :ref:`reference instructions `: |REFNULL|, |REFFUNC|, |REFISNULL| +* New :ref:`reference instructions `: -* Extended :ref:`parametric instruction `: |SELECT| with optional type immediate + - |REFNULL| + - |REFFUNC| + - |REFISNULL| + +* Extended :ref:`parametric instruction `: + + - |SELECT| with optional type immediate * New :ref:`declarative ` form of :ref:`element segment ` @@ -67,7 +80,12 @@ Added instructions to directly access and modify tables. [#proposal-reftype]_ * :ref:`Table types ` allow any :ref:`reference type ` as element type -* New :ref:`table instructions `: |TABLEGET|, |TABLESET|, |TABLESIZE|, |TABLEGROW| +* New :ref:`table instructions `: + + - |TABLEGET| + - |TABLESET| + - |TABLESIZE| + - |TABLEGROW| .. index:: table, instruction, table index, element segment, import, export @@ -77,9 +95,19 @@ Multiple Tables Added the ability to use multiple tables per module. [#proposal-reftype]_ -* :ref:`Modules ` may :ref:`define `, :ref:`import `, and :ref:`export ` multiple tables +* :ref:`Modules ` may -* :ref:`Table instructions ` take a :ref:`table index ` immediate: |TABLEGET|, |TABLESET|, |TABLESIZE|, |TABLEGROW|, |CALLINDIRECT| + - :ref:`define ` multiple tables + - :ref:`import ` multiple tables + - :ref:`export ` multiple tables + +* :ref:`Table instructions ` take a :ref:`table index ` immediate: + + - |TABLEGET| + - |TABLESET| + - |TABLESIZE| + - |TABLEGROW| + - |CALLINDIRECT| * :ref:`Element segments ` take a :ref:`table index ` @@ -91,9 +119,19 @@ Bulk Memory and Table Instructions Added instructions that modify ranges of memory or table entries. [#proposal-reftype]_ [#proposal-bulk]_ -* New :ref:`memory instructions `: |MEMORYFILL|, |MEMORYINIT|, |MEMORYCOPY|, |DATADROP| +* New :ref:`memory instructions `: + + - |MEMORYFILL| + - |MEMORYINIT| + - |MEMORYCOPY| + - |DATADROP| -* New :ref:`table instructions `: |TABLEFILL|, |TABLEINIT|, |TABLECOPY|, |ELEMDROP| +* New :ref:`table instructions `: + + - |TABLEFILL| + - |TABLEINIT| + - |TABLECOPY| + - |ELEMDROP| * New :ref:`passive ` form of :ref:`data segment ` @@ -109,33 +147,128 @@ Added instructions that modify ranges of memory or table entries. [#proposal-ref Vector Instructions ................... -Added vector type and instructions that manipulate multiple numeric values in parallel (also known as *SIMD*, single instruction multiple data) [#proposal-vectype]_ - -* New :ref:`value type `: |V128| - -* New :ref:`memory instructions `: :math:`\K{v128.}\LOAD`, :math:`\K{v128.}\LOAD{}\!N\!\K{x}\!M\!\K{\_}\sx`, :math:`\K{v128.}\LOAD{}N\K{\_zero}`, :math:`\K{v128.}\LOAD{}N\K{\_splat}`, :math:`\K{v128.}\LOAD{}N\K{\_lane}`, :math:`\K{v128.}\STORE`, :math:`\K{v128.}\STORE{}N\K{\_lane}` - -* New constant :ref:`vector instruction `: :math:`\K{v128.}\VCONST` - -* New unary :ref:`vector instructions `: :math:`\K{v128.not}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.abs}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.neg}`, :math:`\K{i8x16.popcnt}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.abs}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.neg}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.sqrt}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.ceil}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.floor}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.trunc}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.nearest}` - -* New binary :ref:`vector instructions `: :math:`\K{v128.and}`, :math:`\K{v128.andnot}`, :math:`\K{v128.or}`, :math:`\K{v128.xor}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.add}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.sub}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.mul}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.add\_sat\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.sub\_sat\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.min\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.max\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.shl}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.shr\_}\sx`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.add}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.extmul\_}\half\K{\_i}\!N'\!\K{x}\!M'\!\K{\_}\sx`, :math:`\K{i16x8.q15mulr\_sat\_s}`, :math:`\K{i32x4.dot\_i16x8\_s}`, :math:`\K{i16x8.extadd\_pairwise\_i8x16\_}\sx`, :math:`\K{i32x4.extadd\_pairwise\_i16x8\_}\sx`, :math:`\K{i8x16.avgr\_u}`, :math:`\K{i16x8.avgr\_u}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.sub}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.mul}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.div}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.min}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.max}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.pmin}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.pmax}` - -* New ternary :ref:`vector instruction `: :math:`\K{v128.bitselect}` - -* New test :ref:`vector instructions `: :math:`\K{v128.any\_true}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.all\_true}` - -* New relational :ref:`vector instructions `: :math:`\K{i}\!N\!\K{x}\!M\!\K{.eq}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.ne}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.lt\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.gt\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.le\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.ge\_}\sx`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.eq}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.ne}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.lt}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.gt}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.le}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.ge}` - -* New conversion :ref:`vector instructions `::math:`\K{i32x4.trunc\_sat\_f32x4\_}\sx`, :math:`\K{i32x4.trunc\_sat\_f64x2\_}\sx\K{\_zero}`, :math:`\K{f32x4.convert\_i32x4\_}\sx`, :math:`\K{f32x4.demote\_f64x2\_zero}`, :math:`\K{f64x2.convert\_low\_i32x4\_}\sx`, :math:`\K{f64x2.promote\_low\_f32x4}` - -* New lane access :ref:`vector instructions `: :math:`\K{i}\!N\!\K{x}\!M\!\K{.extract\_lane\_}\sx^?`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.replace\_lane}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.extract\_lane}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.replace\_lane}` - -* New lane splitting/combining :ref:`vector instructions `: :math:`\K{i}\!N\!\K{x}\!M\!\K{.extend\_}\half\K{\_i}\!N'\!\K{x}\!M'\!\K{\_}\sx`, :math:`\K{i8x16.narrow\_i16x8\_}\sx`, :math:`\K{i16x8.narrow\_i32x4\_}\sx` - -* New byte reordering :ref:`vector instructions `: :math:`\K{i8x16.shuffle}`, :math:`\K{i8x16.swizzle}` - -* New injection/projection :ref:`vector instructions `: :math:`\K{i}\!N\!\K{x}\!M\!\K{.splat}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.splat}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.bitmask}` +Added vector type and instructions that manipulate multiple numeric values in parallel +(also known as *SIMD*, single instruction multiple data) [#proposal-vectype]_ + +* New :ref:`value type `: + + - |V128| + +* New :ref:`memory instructions `: + + - :math:`\K{v128.}\LOAD` + - :math:`\K{v128.}\LOAD{}\!N\!\K{x}\!M\!\K{\_}\sx` + - :math:`\K{v128.}\LOAD{}N\K{\_zero}` + - :math:`\K{v128.}\LOAD{}N\K{\_splat}` + - :math:`\K{v128.}\LOAD{}N\K{\_lane}` + - :math:`\K{v128.}\STORE` + - :math:`\K{v128.}\STORE{}N\K{\_lane}` + +* New constant :ref:`vector instruction `: + + - :math:`\K{v128.}\VCONST` + +* New unary :ref:`vector instructions `: + + - :math:`\K{v128.not}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.abs}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.neg}` + - :math:`\K{i8x16.popcnt}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.abs}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.neg}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.sqrt}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.ceil}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.floor}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.trunc}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.nearest}` + +* New binary :ref:`vector instructions `: + + - :math:`\K{v128.and}` + - :math:`\K{v128.andnot}` + - :math:`\K{v128.or}` + - :math:`\K{v128.xor}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.add}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.sub}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.mul}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.add\_sat\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.sub\_sat\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.min\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.max\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.shl}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.shr\_}\sx` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.add}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.sub}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.mul}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.div}` + - :math:`\K{i16x8.extadd\_pairwise\_i8x16\_}\sx` + - :math:`\K{i32x4.extadd\_pairwise\_i16x8\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.extmul\_}\half\K{\_i}\!N'\!\K{x}\!M'\!\K{\_}\sx` + - :math:`\K{i16x8.q15mulr\_sat\_s}` + - :math:`\K{i32x4.dot\_i16x8\_s}` + - :math:`\K{i8x16.avgr\_u}` + - :math:`\K{i16x8.avgr\_u}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.min}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.max}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.pmin}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.pmax}` + +* New ternary :ref:`vector instruction `: + + - :math:`\K{v128.bitselect}` + +* New test :ref:`vector instructions `: + + - :math:`\K{v128.any\_true}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.all\_true}` + +* New relational :ref:`vector instructions `: + + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.eq}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.ne}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.lt\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.gt\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.le\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.ge\_}\sx` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.eq}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.ne}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.lt}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.gt}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.le}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.ge}` + +* New conversion :ref:`vector instructions `: + + - :math:`\K{i32x4.trunc\_sat\_f32x4\_}\sx` + - :math:`\K{i32x4.trunc\_sat\_f64x2\_}\sx\K{\_zero}` + - :math:`\K{f32x4.convert\_i32x4\_}\sx` + - :math:`\K{f32x4.demote\_f64x2\_zero}` + - :math:`\K{f64x2.convert\_low\_i32x4\_}\sx` + - :math:`\K{f64x2.promote\_low\_f32x4}` + +* New lane access :ref:`vector instructions `: + + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.extract\_lane\_}\sx^?` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.replace\_lane}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.extract\_lane}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.replace\_lane}` + +* New lane splitting/combining :ref:`vector instructions `: + + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.extend\_}\half\K{\_i}\!N'\!\K{x}\!M'\!\K{\_}\sx` + - :math:`\K{i8x16.narrow\_i16x8\_}\sx` + - :math:`\K{i16x8.narrow\_i32x4\_}\sx` + +* New byte reordering :ref:`vector instructions `: + + - :math:`\K{i8x16.shuffle}` + - :math:`\K{i8x16.swizzle}` + +* New injection/projection :ref:`vector instructions `: + + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.splat}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.splat}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.bitmask}` .. [#proposal-signext] @@ -167,7 +300,12 @@ Extended Constant Expressions Allowed basic numeric computations in constant expressions. [#proposal-extconst]_ -* Extended set of :ref:`constant instructions ` with :math:`\K{i}\X{nn}\K{.add}`, :math:`\K{i}\X{nn}\K{.sub}`, and :math:`\K{i}\X{nn}\K{.mul}`, and |GLOBALGET| for any previously declared immutable :ref:`global ` +* Extended set of :ref:`constant instructions ` with: + + - :math:`\K{i}\X{nn}\K{.add}` + - :math:`\K{i}\X{nn}\K{.sub}` + - :math:`\K{i}\X{nn}\K{.mul}` + - |GLOBALGET| for any previously declared immutable :ref:`global ` .. note:: The :ref:`garbage collection ` added further constant instructions. @@ -180,7 +318,10 @@ Tail Calls Added instructions to perform tail calls. [#proposal-tailcall]_ -* New :ref:`control instructions `: |RETURNCALL| and |RETURNCALLINDIRECT| +* New :ref:`control instructions `: + + - |RETURNCALL| + - |RETURNCALLINDIRECT| .. index:: instruction, exception, reference type, tag type, tag, handler @@ -190,13 +331,27 @@ Exception Handling Added tag definitions, imports, and exports, and instructions to throw and catch exceptions [#proposal-exn]_ -* Modules may :ref:`define `, :ref:`import `, and :ref:`export ` tags. +* :ref:`Modules ` may + + - :ref:`define ` tags + - :ref:`import ` tags + - :ref:`export ` tags + +* New :ref:`heap types `: + + - |EXN| + - |NOEXN| + +* New :ref:`reference type ` short-hands: -* New :ref:`heap types `: |EXN|, |NOEXN| + - |EXNREF| + - |NULLEXNREF| -* New :ref:`reference type ` short-hands: |EXNREF|, |NULLEXNREF| +* New :ref:`control instructions `: -* New :ref:`control instructions `: |THROW|, |THROWREF|, and |TRYTABLE|. + - |THROW| + - |THROWREF| + - |TRYTABLE| * New :ref:`tag section ` in binary format. @@ -208,9 +363,28 @@ Multiple Memories Added the ability to use multiple memories per module. [#proposal-multimem]_ -* :ref:`Modules ` may :ref:`define `, :ref:`import `, and :ref:`export ` multiple memories - -* :ref:`Memory instructions ` take a :ref:`memory index ` immediate: |MEMORYSIZE|, |MEMORYGROW|, |MEMORYFILL|, |MEMORYCOPY|, |MEMORYINIT|, :math:`t\K{.load}`, :math:`t\K{.store}`, :math:`t\K{.load}\!N\!\K{\_}\sx`, :math:`t\K{.store}\!N`, :math:`\K{v128.load}\!N\!\K{x}\!M\!\K{\_}\sx`, :math:`\K{v128.load}\!N\!\K{\_zero}`, :math:`\K{v128.load}\!N\!\K{\_splat}`, :math:`\K{v128.load}\!N\!\K{\_lane}`, :math:`\K{v128.store}\!N\!\K{\_lane}` +* :ref:`Modules ` may + + - :ref:`define ` multiple memories + - :ref:`import ` multiple memories + - :ref:`export ` multiple memories + +* :ref:`Memory instructions ` take a :ref:`memory index ` immediate: + + - |MEMORYSIZE| + - |MEMORYGROW| + - |MEMORYFILL| + - |MEMORYCOPY| + - |MEMORYINIT| + - :math:`t\K{.load}` + - :math:`t\K{.store}` + - :math:`t\K{.load}\!N\!\K{\_}\sx` + - :math:`t\K{.store}\!N` + - :math:`\K{v128.load}\!N\!\K{x}\!M\!\K{\_}\sx` + - :math:`\K{v128.load}\!N\!\K{\_zero}` + - :math:`\K{v128.load}\!N\!\K{\_splat}` + - :math:`\K{v128.load}\!N\!\K{\_lane}` + - :math:`\K{v128.store}\!N\!\K{\_lane}` * :ref:`Data segments ` take a :ref:`memory index ` @@ -222,17 +396,31 @@ Typeful References Added more precise types for references. [#proposal-typedref]_ -* New generalised form of :ref:`reference types `: :math:`(\REF~\NULL^?~\heaptype)` +* New generalised form of :ref:`reference types `: + + - :math:`(\REF~\NULL^?~\heaptype)` + +* New class of :ref:`heap types `: -* New class of :ref:`heap types `: |FUNC|, |EXTERN|, :math:`\typeidx` + - |FUNC| + - |EXTERN| + - :math:`\typeidx` * Basic :ref:`subtyping ` on :ref:`reference ` and :ref:`value ` types -* New :ref:`reference instructions `: |REFASNONNULL|, |BRONNULL|, |BRONNONNULL| +* New :ref:`reference instructions `: + + - |REFASNONNULL| + - |BRONNULL| + - |BRONNONNULL| + +* New :ref:`control instruction `: -* New :ref:`control instruction `: |CALLREF| + - |CALLREF| -* Refined typing of :ref:`reference instruction ` |REFFUNC| with more precise result type +* Refined typing of :ref:`reference instruction `: + + - |REFFUNC| with more precise result type * Refined typing of :ref:`local instructions ` and :ref:`instruction sequences ` to track the :ref:`initialization status ` of :ref:`locals ` with non-:ref:`defaultable ` type @@ -247,25 +435,87 @@ Garbage Collection Added managed reference types. [#proposal-gc]_ -* New forms of :ref:`heap types `: |ANY|, |EQT|, |I31|, |STRUCT|, |ARRAY|, |NONE|, |NOFUNC|, |NOEXTERN| +* New forms of :ref:`heap types `: + + - |ANY| + - |EQT| + - |I31| + - |STRUCT| + - |ARRAY| + - |NONE| + - |NOFUNC| + - |NOEXTERN| + +* New :ref:`reference type ` short-hands: -* New :ref:`reference type ` short-hands: |ANYREF|, |EQREF|, |I31REF|, |STRUCTREF|, |ARRAYREF|, |NULLREF|, |NULLFUNCREF|, |NULLEXTERNREF| + - |ANYREF| + - |EQREF| + - |I31REF| + - |STRUCTREF| + - |ARRAYREF| + - |NULLREF| + - |NULLFUNCREF| + - |NULLEXTERNREF| -* New forms of type definitions: :ref:`structure ` and :ref:`array types `, :ref:`sub types `, and :ref:`recursive types ` +* New forms of type definitions: + + - :ref:`structure ` + - :ref:`array types ` + - :ref:`sub types ` + - :ref:`recursive types ` * Enriched :ref:`subtyping ` based on explicitly declared :ref:`sub types ` and the new heap types -* New generic :ref:`reference instructions `: |REFEQ|, |REFTEST|, |REFCAST|, |BRONCAST|, |BRONCASTFAIL| +* New generic :ref:`reference instructions `: + + - |REFEQ| + - |REFTEST| + - |REFCAST| + - |BRONCAST| + - |BRONCASTFAIL| + +* New :ref:`reference instructions ` for :ref:`unboxed scalars `: -* New :ref:`reference instructions ` for :ref:`unboxed scalars `: |REFI31|, :math:`\I31GET\K{\_}\sx` + - |REFI31| + - :math:`\I31GET\K{\_}\sx` -* New :ref:`reference instructions ` for :ref:`structure types `: |STRUCTNEW|, |STRUCTNEWDEFAULT|, :math:`\STRUCTGET\K{\_}\sx^?`, |STRUCTSET| +* New :ref:`reference instructions ` for :ref:`structure types `: -* New :ref:`reference instructions ` for :ref:`array types `: |ARRAYNEW|, |ARRAYNEWDEFAULT|, |ARRAYNEWFIXED|, |ARRAYNEWDATA|, |ARRAYNEWELEM|, :math:`\ARRAYGET\K{\_}\sx^?`, |ARRAYSET|, |ARRAYLEN|, |ARRAYFILL|, |ARRAYCOPY|, |ARRAYINITDATA|, |ARRAYINITELEM| + - |STRUCTNEW| + - |STRUCTNEWDEFAULT| + - :math:`\STRUCTGET\K{\_}\sx^?` + - |STRUCTSET| -* New :ref:`reference instructions ` for converting :ref:`host types `: |ANYCONVERTEXTERN|, |EXTERNCONVERTANY| +* New :ref:`reference instructions ` for :ref:`array types `: -* Extended set of :ref:`constant instructions ` with |REFI31|, |STRUCTNEW|, |STRUCTNEWDEFAULT|, |ARRAYNEW|, |ARRAYNEWDEFAULT|, |ARRAYNEWFIXED|, |ANYCONVERTEXTERN|, |EXTERNCONVERTANY| + - |ARRAYNEW| + - |ARRAYNEWDEFAULT| + - |ARRAYNEWFIXED| + - |ARRAYNEWDATA| + - |ARRAYNEWELEM| + - :math:`\ARRAYGET\K{\_}\sx^?` + - |ARRAYSET| + - |ARRAYLEN| + - |ARRAYFILL| + - |ARRAYCOPY| + - |ARRAYINITDATA| + - |ARRAYINITELEM| + +* New :ref:`reference instructions ` for converting :ref:`external types `: + + - |ANYCONVERTEXTERN| + - |EXTERNCONVERTANY| + +* Extended set of :ref:`constant instructions ` with: + + - |REFI31| + - |STRUCTNEW| + - |STRUCTNEWDEFAULT| + - |ARRAYNEW| + - |ARRAYNEWDEFAULT| + - |ARRAYNEWFIXED| + - |ANYCONVERTEXTERN| + - |EXTERNCONVERTANY| .. index:: text format, annotation, custom section, identifier, module, type, function, local, structure field @@ -280,7 +530,13 @@ mirroring the role of custom sections in the binary format. [#proposal-annot]_ * :ref:`Identifiers ` can be escaped as :math:`\text{@"\dots"}` with arbitrary :ref:`names ` -* Defined :ref:`name annotations ` :math:`\text{(@name~"\dots")}` for :ref:`module names `, :ref:`type names `, :ref:`function names `, :ref:`local names `, and :ref:`field names ` +* Defined :ref:`name annotations ` :math:`\text{(@name~"\dots")}` for: + + - :ref:`module names ` + - :ref:`type names ` + - :ref:`function names ` + - :ref:`local names ` + - :ref:`field names ` * Defined :ref:`custom annotation ` :math:`\text{(@custom~"\dots")}` to represent arbitrary :ref:`custom sections ` in the text format diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index 7fd8bb5d2..aac9f7609 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -4114,57 +4114,57 @@ Control Instructions :math:`\THROWREF` ................. -1. Let :math:`F` be the :ref:`current ` :ref:`frame `. - -2. Assert: due to :ref:`validation `, a :ref:`reference ` is on the top of the stack. +1. Assert: due to :ref:`validation `, a :ref:`reference ` is on the top of the stack. -3. Pop the reference :math:`\reff` from the stack. +2. Pop the reference :math:`\reff` from the stack. -4. If :math:`\reff` is :math:`\REFNULL~\X{ht}`, then: +3. If :math:`\reff` is :math:`\REFNULL~\X{ht}`, then: a. Trap. -5. Assert: due to :ref:`validation `, :math:`\reff` is an :ref:`exception reference `. +4. Assert: due to :ref:`validation `, :math:`\reff` is an :ref:`exception reference `. -6. Let :math:`\REFEXNADDR~\X{ea}` be :math:`\reff`. +5. Let :math:`\REFEXNADDR~\X{ea}` be :math:`\reff`. -7. Assert: due to :ref:`validation `, :math:`S.\SEXNS[\X{ea}]` exists. +6. Assert: due to :ref:`validation `, :math:`S.\SEXNS[\X{ea}]` exists. -8. Let :math:`\X{exn}` be the :ref:`exception instance ` :math:`S.\SEXNS[\X{ea}]`. +7. Let :math:`\X{exn}` be the :ref:`exception instance ` :math:`S.\SEXNS[\X{ea}]`. -9. Let :math:`a` be the :ref:`tag address ` :math:`\X{exn}.\EITAG`. +8. Let :math:`a` be the :ref:`tag address ` :math:`\X{exn}.\EITAG`. -10. While the stack is not empty and the top of the stack is not an :ref:`exception handler `, do: +9. While the stack is not empty and the top of the stack is not an :ref:`exception handler `, do: a. Pop the top element from the stack. -11. Assert: the stack is now either empty, or there is an exception handler on the top of the stack. +10. Assert: the stack is now either empty, or there is an exception handler on the top of the stack. -12. If the stack is empty, then: +11. If the stack is empty, then: a. Return the exception :math:`(\REFEXNADDR~a)` as a :ref:`result `. -13. Assert: there is an :ref:`exception handler ` on the top of the stack. +12. Assert: there is an :ref:`exception handler ` on the top of the stack. -14. Pop the exception handler :math:`\HANDLER_n\{\catch^\ast\}` from the stack. +13. Pop the exception handler :math:`\HANDLER_n\{\catch^\ast\}` from the stack. -15. If :math:`\catch^\ast` is empty, then: +14. If :math:`\catch^\ast` is empty, then: a. Push the exception reference :math:`\REFEXNADDR~\X{ea}` back to the stack. b. Execute the instruction |THROWREF| again. -16. Else: +15. Else: + + a. Let :math:`F` be the :ref:`current ` :ref:`frame `. - a. Let :math:`\catch_1` be the first :ref:`catch clause ` in :math:`\catch^\ast` and :math:`{\catch'}^\ast` the remaining clauses. + b. Let :math:`\catch_1` be the first :ref:`catch clause ` in :math:`\catch^\ast` and :math:`{\catch'}^\ast` the remaining clauses. - b. If :math:`\catch_1` is of the form :math:`\CATCH~x~l` and the :ref:`tag address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: + c. If :math:`\catch_1` is of the form :math:`\CATCH~x~l` and the :ref:`tag address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: i. Push the values :math:`\X{exn}.\EIFIELDS` to the stack. ii. Execute the instruction :math:`\BR~l`. - c. Else if :math:`\catch_1` is of the form :math:`\CATCHREF~x~l` and the :ref:`tag address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: + d. Else if :math:`\catch_1` is of the form :math:`\CATCHREF~x~l` and the :ref:`tag address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: i. Push the values :math:`\X{exn}.\EIFIELDS` to the stack. @@ -4172,17 +4172,17 @@ Control Instructions iii. Execute the instruction :math:`\BR~l`. - d. Else if :math:`\catch_1` is of the form :math:`\CATCHALL~l`, then: + e. Else if :math:`\catch_1` is of the form :math:`\CATCHALL~l`, then: i. Execute the instruction :math:`\BR~l`. - e. Else if :math:`\catch_1` is of the form :math:`\CATCHALLREF~l`, then: + f. Else if :math:`\catch_1` is of the form :math:`\CATCHALLREF~l`, then: i. Push the exception reference :math:`\REFEXNADDR~\X{ea}` to the stack. ii. Execute the instruction :math:`\BR~l`. - f. Else: + g. Else: 1. Push the modified handler :math:`\HANDLER_n\{{\catch'}^\ast\}` back to the stack. @@ -4783,9 +4783,9 @@ The following auxiliary rules define the semantics of entering and exiting |TRYT Entering :math:`\instr^\ast` with label :math:`L` and exception handler :math:`H` ................................................................................. -1. Push :math:`L` to the stack. +1. Push :math:`H` to the stack. -2. Push :math:`H` onto the stack. +2. Push :math:`L` onto the stack. 3. Jump to the start of the instruction sequence :math:`\instr^\ast`. diff --git a/document/core/text/instructions.rst b/document/core/text/instructions.rst index d3f7935c6..4c857db99 100644 --- a/document/core/text/instructions.rst +++ b/document/core/text/instructions.rst @@ -950,6 +950,7 @@ Vector constant instructions have a mandatory :ref:`shape ` de \text{i32x4.all\_true} &\Rightarrow& \I32X4.\ALLTRUE\\ &&|& \text{i32x4.bitmask} &\Rightarrow& \I32X4.\BITMASK\\ &&|& \text{i32x4.extadd\_pairwise\_i16x8\_s} &\Rightarrow& \I32X4.\EXTADDPAIRWISE\K{\_i16x8\_s}\\ &&|& + \text{i32x4.extadd\_pairwise\_i16x8\_u} &\Rightarrow& \I32X4.\EXTADDPAIRWISE\K{\_i16x8\_u}\\ &&|& \text{i32x4.extend\_low\_i16x8\_s} &\Rightarrow& \I32X4.\VEXTEND\K{\_low\_i16x8\_s}\\ &&|& \text{i32x4.extend\_high\_i16x8\_s} &\Rightarrow& \I32X4.\VEXTEND\K{\_high\_i16x8\_s}\\ &&|& \text{i32x4.extend\_low\_i16x8\_u} &\Rightarrow& \I32X4.\VEXTEND\K{\_low\_i16x8\_u}\\ &&|& diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index e47bdb576..43b55df47 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -2044,7 +2044,7 @@ Control Instructions * The tag :math:`C.\CTAGS[x]` must be defined in the context. -* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`tag type ` :math:`C.\CTAGS[x]`. +* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`expansion ` of the :ref:`tag type ` :math:`C.\CTAGS[x]`. * The :ref:`result type ` :math:`[{t'}^\ast]` must be empty. @@ -2056,7 +2056,7 @@ Control Instructions .. math:: \frac{ - C.\CTAGS[x] = [t^\ast] \toF [] + \expanddt(C.\CTAGS[x]) = [t^\ast] \toF [] \qquad C.\CLABELS[l] = [t^\ast] }{ @@ -2068,21 +2068,21 @@ Control Instructions * The tag :math:`C.\CTAGS[x]` must be defined in the context. -* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`tag type ` :math:`C.\CTAGS[x]`. +* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`expansion ` of the :ref:`tag type ` :math:`C.\CTAGS[x]`. * The :ref:`result type ` :math:`[{t'}^\ast]` must be empty. * The label :math:`C.\CLABELS[l]` must be defined in the context. -* The :ref:`result type ` :math:`[t^\ast]` must be the same as :math:`C.\CLABELS[l]` with |EXNREF| appended. +* The :ref:`result type ` :math:`[t^\ast~(\REF~\EXN)]` must :ref:`match ` :math:`C.\CLABELS[l]`. * Then the catch clause is valid. .. math:: \frac{ - C.\CTAGS[x] = [t^\ast] \toF [] + \expanddt(C.\CTAGS[x]) = [t^\ast] \toF [] \qquad - C.\CLABELS[l] = [t^\ast~\EXNREF] + C \vdashresulttypematch [t^\ast~(\REF~\EXN)] \matchesresulttype C.\CLABELS[l] }{ C \vdashcatch \CATCHREF~x~l \ok } @@ -2108,13 +2108,13 @@ Control Instructions * The label :math:`C.\CLABELS[l]` must be defined in the context. -* The :ref:`result type ` :math:`C.\CLABELS[l] must be :math:`[\EXNREF]`. +* The :ref:`result type ` :math:`[(\REF~\EXN)]` must :ref:`match ` :math:`C.\CLABELS[l]`. * Then the catch clause is valid. .. math:: \frac{ - C.\CLABELS[l] = [\EXNREF] + C \vdashresulttypematch [(\REF~\EXN)] \matchesresulttype C.\CLABELS[l] }{ C \vdashcatch \CATCHALLREF~l \ok } @@ -2343,7 +2343,7 @@ Control Instructions * Let :math:`[t^\ast]` be the :ref:`result type ` of :math:`C.\CRETURN`. -* Then the instruction is valid with any :ref:`valid ` type of the form :math:`[t_1^\ast] \to [t_2^\ast]`. +* Then the instruction is valid with any :ref:`valid ` type of the form :math:`[t_1^\ast~t^\ast] \to [t_2^\ast]`. .. math:: \frac{ diff --git a/document/index.html b/document/index.html index f7661d8be..c78ff112c 100644 --- a/document/index.html +++ b/document/index.html @@ -14,13 +14,20 @@
+

WebAssembly Specifications

To support the embedding of WebAssembly into different environments, its specification is split into layers that are specified in separate documents.

-

Core specification

+

+ Source for these documents is available + here. +

+ + +

Core Specification

Defines the semantics of WebAssembly modules independent from a concrete embedding. The WebAssembly core is specified in a single document:

@@ -41,12 +48,13 @@

Core specification

-

Embedder specifications

+ +

Embedder Specifications

Define application programming interfaces (APIs) enabling the use of WebAssembly modules in concrete embedding environments. Currently, two APIs are specified:

-
    +
    • JavaScript Embedding: defines JavaScript classes and objects for accessing WebAssembly from within JavaScript, including methods for validation, compilation, instantiation, and classes for representing and manipulating imports and exports as JavaScript objects.

      • W3C version
      • @@ -60,7 +68,8 @@

        Embedder specifications

    • -
    +
+

Metadata specifications

@@ -97,10 +106,14 @@

Legacy Extensions

-

- Source for all documents is available - here. -

+ +

All Versions

+ +
diff --git a/document/js-api/Makefile b/document/js-api/Makefile index df8b7097c..7893918d4 100644 --- a/document/js-api/Makefile +++ b/document/js-api/Makefile @@ -5,11 +5,12 @@ DOWNLOADDIR = _download NAME = WebAssembly DECISION_URL = https://github.com/WebAssembly/meetings/blob/main/main/2024/WG-06-12.md TAR = tar +DEADLINE = $(shell date -d "+30 days" +%Y-%m-%d 2>/dev/null || date -v +30d +%Y-%m-%d) .PHONY: all all: mkdir -p $(BUILDDIR)/html - bikeshed spec --md-status=$(W3C_STATUS) index.bs $(BUILDDIR)/html/index.html + bikeshed spec --md-status=$(W3C_STATUS) --md-deadline=$(DEADLINE) index.bs $(BUILDDIR)/html/index.html @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." .PHONY: publish @@ -34,7 +35,7 @@ diff: all # macOS tar has no “--transform” option (only GNU tar does), so on macOS, # do “brew install tar” & run “make” like this: “TAR=gtar make -e WD-tar” WD-tar: all - bikeshed spec --md-status=$(W3C_STATUS) index.bs $(BUILDDIR)/html/index.html + bikeshed spec --md-status=$(W3C_STATUS) --md-deadline=$(DEADLINE) index.bs $(BUILDDIR)/html/index.html $(TAR) -C $(BUILDDIR)/html --transform="s/index.html/Overview.html/" -cf $(BUILDDIR)/WD.tar index.html @echo "Built $(BUILDDIR)/WD.tar." @@ -63,4 +64,4 @@ WD-echidna-CI: WD-tar -F "token=$(W3C_ECHIDNA_TOKEN_JSAPI)" \ -F "decision=$(DECISION_URL)" | tee $(BUILDDIR)/WD-echidna-id.txt @echo - @echo "Published w$(W3C_STATUS). Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" + @echo "Published $(W3C_STATUS). Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" diff --git a/document/versions/Makefile b/document/versions/Makefile new file mode 100644 index 000000000..c65f94b9e --- /dev/null +++ b/document/versions/Makefile @@ -0,0 +1,23 @@ +DIRS = core +DIRNAME = versions +BUILDDIR = _build + +.PHONY: all +all: + mkdir -p $(BUILDDIR)/html/$(DIRNAME) + cp -R $(DIRS) $(BUILDDIR)/html/$(DIRNAME)/ + +.PHONY: WD-tar +WD-tar: + +.PHONY: WD-echidna +WD-echidna: + +.PHONY: WD-echidna-CI +WD-echidna-CI: + +.PHONY: diff +diff: + +.PHONY: clean +clean: diff --git a/document/versions/core/WebAssembly-1.0.pdf b/document/versions/core/WebAssembly-1.0.pdf new file mode 100644 index 000000000..456d91c95 Binary files /dev/null and b/document/versions/core/WebAssembly-1.0.pdf differ diff --git a/document/versions/core/WebAssembly-2.0.pdf b/document/versions/core/WebAssembly-2.0.pdf new file mode 100644 index 000000000..3a43c0020 Binary files /dev/null and b/document/versions/core/WebAssembly-2.0.pdf differ diff --git a/document/versions/core/WebAssembly-3.0-draft.pdf b/document/versions/core/WebAssembly-3.0-draft.pdf new file mode 100644 index 000000000..3141db529 Binary files /dev/null and b/document/versions/core/WebAssembly-3.0-draft.pdf differ diff --git a/document/web-api/Makefile b/document/web-api/Makefile index c5bda0b7a..2ea8f11ac 100644 --- a/document/web-api/Makefile +++ b/document/web-api/Makefile @@ -5,11 +5,12 @@ DOWNLOADDIR = _download NAME = WebAssembly DECISION_URL = https://github.com/WebAssembly/meetings/blob/main/main/2024/WG-06-12.md TAR = tar +DEADLINE = $(shell date -d "+30 days" +%Y-%m-%d 2>/dev/null || date -v +30d +%Y-%m-%d) .PHONY: all all: mkdir -p $(BUILDDIR)/html - bikeshed spec --md-status=$(W3C_STATUS) index.bs $(BUILDDIR)/html/index.html + bikeshed spec --md-status=$(W3C_STATUS) --md-deadline=$(DEADLINE) index.bs $(BUILDDIR)/html/index.html @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." .PHONY: publish @@ -34,7 +35,7 @@ diff: all # macOS tar has no “--transform” option (only GNU tar does), so on macOS, # do “brew install tar” & run “make” like this: “TAR=gtar make -e WD-tar” WD-tar: all - bikeshed spec --md-status=$(W3C_STATUS) index.bs $(BUILDDIR)/html/index.html + bikeshed spec --md-status=$(W3C_STATUS) --md-deadline=$(DEADLINE) index.bs $(BUILDDIR)/html/index.html $(TAR) -C $(BUILDDIR)/html --transform="s/index.html/Overview.html/" -cf $(BUILDDIR)/WD.tar index.html @echo "Built $(BUILDDIR)/WD.tar." diff --git a/interpreter/README.md b/interpreter/README.md index 8aa7e9ae1..fc37dc14f 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -426,8 +426,9 @@ In order to be able to check and run modules for testing purposes, the S-express script: * cmd: - ;; define, validate, and initialize module - ( register ? ) ;; register module for imports + ;; define, validate, and possibly instantiate module + + ( register ? ) ;; register module instance for imports ;; perform action and print results ;; assert result of an action ;; meta command @@ -436,6 +437,10 @@ module: ... ( module ? binary * ) ;; module in binary format (may be malformed) ( module ? quote * ) ;; module quoted in text (may be malformed) + ( module definition ? binary ... ) ;; uninstantiated module + +instance: + ( module instance ? ? ) ;; instantiate latter module to former instance action: ( invoke ? * ) ;; invoke function export @@ -455,8 +460,8 @@ assertion: ( assert_exhaustion ) ;; assert action exhausts system resources ( assert_malformed ) ;; assert module cannot be decoded with given failure string ( assert_invalid ) ;; assert module is invalid with given failure string - ( assert_unlinkable ) ;; assert module fails to link - ( assert_trap ) ;; assert module traps on instantiation + ( assert_unlinkable ) ;; assert module fails to link + ( assert_trap ) ;; assert module traps on instantiation result_pat: ( .const ) @@ -485,6 +490,11 @@ The script format supports additional syntax for defining modules. A module of the form `(module binary *)` is given in binary form and will be decoded from the (concatenation of the) strings. A module of the form `(module quote *)` is given in textual form and will be parsed from the (concatenation of the) strings. In both cases, decoding/parsing happens when the command is executed, not when the script is parsed, so that meta commands like `assert_malformed` can be used to check expected errors. +Usually, a module declaration implicitly instantiates the module, +that is, it defines both a module and an instance (of the same name). +Instantiation can be suppressed by adding the keyword `definition`. +A module declared as a definition only can then be instantiated explicitly, and multiple times, using the separate form `(module instance )`. + There are also a number of meta commands. The `script` command is a simple mechanism to name sub-scripts themselves. This is mainly useful for converting scripts with the `output` command. Commands inside a `script` will be executed normally, but nested meta are expanded in place (`input`, recursively) or elided (`output`) in the named script. @@ -492,6 +502,7 @@ The `input` and `output` meta commands determine the requested file format from The interpreter supports a "dry" mode (flag `-d`), in which modules are only validated. In this mode, all actions and assertions are ignored. It also supports an "unchecked" mode (flag `-u`), in which module definitions are not validated before use. +In that mode, execution may fail with a "crash" error message. ### Spectest host module @@ -537,6 +548,10 @@ cmd: module: ( module ? binary * ) ;; module in binary format (may be malformed) + ( module definition ? binary ... ) ;; uninstantiated module + +instance: + ( module instance ? ? ) ;; instantiate latter module to former instance action: ( invoke ? * ) ;; invoke function export @@ -549,8 +564,8 @@ assertion: ( assert_exhaustion ) ;; assert action exhausts system resources ( assert_malformed ) ;; assert module cannot be decoded with given failure string ( assert_invalid ) ;; assert module is invalid with given failure string - ( assert_unlinkable ) ;; assert module fails to link - ( assert_trap ) ;; assert module traps on instantiation + ( assert_unlinkable ) ;; assert module fails to link + ( assert_trap ) ;; assert module traps on instantiation result_pat: ( .const ) diff --git a/interpreter/script/js.ml b/interpreter/script/js.ml index 0a4a60537..7bacb5182 100644 --- a/interpreter/script/js.ml +++ b/interpreter/script/js.ml @@ -68,8 +68,8 @@ function module(bytes, valid = true) { return new WebAssembly.Module(buffer); } -function instance(bytes, imports = registry) { - return new WebAssembly.Instance(module(bytes), imports); +function instance(mod, imports = registry) { + return new WebAssembly.Instance(mod, imports); } function call(instance, name, args) { @@ -111,16 +111,14 @@ function assert_invalid_custom(bytes) { return; } -function assert_unlinkable(bytes) { - let mod = module(bytes); +function assert_unlinkable(mod) { try { new WebAssembly.Instance(mod, registry) } catch (e) { if (e instanceof WebAssembly.LinkError) return; } throw new Error("Wasm linking failure expected"); } -function assert_uninstantiable(bytes) { - let mod = module(bytes); +function assert_uninstantiable(mod) { try { new WebAssembly.Instance(mod, registry) } catch (e) { if (e instanceof WebAssembly.RuntimeError) return; } @@ -167,7 +165,7 @@ function assert_return(action, ...expected) { // Note that JS can't reliably distinguish different NaN values, // so there's no good way to test that it's a canonical NaN. if (!Number.isNaN(actual[i])) { - throw new Error("Wasm return value NaN expected, got " + actual[i]); + throw new Error("Wasm NaN return value expected, got " + actual[i]); }; return; case "ref.i31": @@ -182,7 +180,7 @@ function assert_return(action, ...expected) { // For now, JS can't distinguish exported Wasm GC values, // so we only test for object. if (typeof actual[i] !== "object") { - throw new Error("Wasm function return value expected, got " + actual[i]); + throw new Error("Wasm object return value expected, got " + actual[i]); }; return; case "ref.func": @@ -216,35 +214,63 @@ module NameMap = Map.Make(struct type t = Ast.name let compare = compare end) module Map = Map.Make(String) type exports = extern_type NameMap.t -type modules = {mutable env : exports Map.t; mutable current : int} +type env = + { mutable mods : exports Map.t; + mutable insts : exports Map.t; + mutable current_mod : int; + mutable current_inst : int; + } let exports m : exports = let ModuleT (_, ets) = module_type_of m in List.fold_left (fun map (ExportT (et, name)) -> NameMap.add name et map) NameMap.empty ets -let modules () : modules = {env = Map.empty; current = 0} +let env () : env = + { mods = Map.empty; + insts = Map.empty; + current_mod = 0; + current_inst = 0; + } + +let current_mod (env : env) = "$$" ^ string_of_int env.current_mod +let of_mod_opt (env : env) = function + | None -> current_mod env + | Some x -> x.it -let current_var (mods : modules) = "$" ^ string_of_int mods.current -let of_var_opt (mods : modules) = function - | None -> current_var mods +let current_inst (env : env) = "$" ^ string_of_int env.current_inst +let of_inst_opt (env : env) = function + | None -> current_inst env | Some x -> x.it -let bind (mods : modules) x_opt m = +let bind_mod (env : env) x_opt m = let exports = exports m in - mods.current <- mods.current + 1; - mods.env <- Map.add (of_var_opt mods x_opt) exports mods.env; - if x_opt <> None then mods.env <- Map.add (current_var mods) exports mods.env - -let lookup (mods : modules) x_opt name at = - let exports = - try Map.find (of_var_opt mods x_opt) mods.env with Not_found -> - raise (Eval.Crash (at, - if x_opt = None then "no module defined within script" - else "unknown module " ^ of_var_opt mods x_opt ^ " within script")) - in try NameMap.find name exports with Not_found -> + env.current_mod <- env.current_mod + 1; + env.mods <- Map.add (of_mod_opt env x_opt) exports env.mods; + if x_opt <> None then env.mods <- Map.add (current_mod env) exports env.mods + +let bind_inst (env : env) x_opt exports = + env.current_inst <- env.current_inst + 1; + env.insts <- Map.add (of_inst_opt env x_opt) exports env.insts; + if x_opt <> None then env.insts <- Map.add (current_inst env) exports env.insts + +let find_mod (env : env) x_opt at = + try Map.find (of_mod_opt env x_opt) env.mods with Not_found -> + raise (Eval.Crash (at, + if x_opt = None then "no module defined within script" + else "unknown module " ^ of_mod_opt env x_opt ^ " within script")) + +let find_inst (env : env) x_opt at = + try Map.find (of_inst_opt env x_opt) env.insts with Not_found -> + raise (Eval.Crash (at, + if x_opt = None then "no module instance defined within script" + else "unknown module instance " ^ of_inst_opt env x_opt ^ " within script")) + +let lookup_export (env : env) x_opt name at = + let exports = find_inst env x_opt at in + try NameMap.find name exports with Not_found -> raise (Eval.Crash (at, "unknown export \"" ^ - string_of_name name ^ "\" within module")) + string_of_name name ^ "\" within module isntance")) (* Wrappers *) @@ -573,37 +599,40 @@ let rec of_definition def = try of_definition (snd (Parse.Module.parse_string ~offset:s.at s.it)) with Parse.Syntax _ | Custom.Syntax _ -> of_bytes "" -let of_wrapper mods x_opt name wrap_action wrap_assertion at = - let x = of_var_opt mods x_opt in +let of_instance env x_opt = + "instance(" ^ of_mod_opt env x_opt ^ ")" + +let of_wrapper env x_opt name wrap_action wrap_assertion at = + let x = of_inst_opt env x_opt in let bs = wrap name wrap_action wrap_assertion at in - "call(instance(" ^ of_bytes bs ^ ", " ^ + "call(instance(module(" ^ of_bytes bs ^ "), " ^ "exports(" ^ x ^ ")), " ^ " \"run\", [])" -let of_action mods act = +let of_action env act = match act.it with | Invoke (x_opt, name, vs) -> - "call(" ^ of_var_opt mods x_opt ^ ", " ^ of_name name ^ ", " ^ + "call(" ^ of_inst_opt env x_opt ^ ", " ^ of_name name ^ ", " ^ "[" ^ String.concat ", " (List.map of_value vs) ^ "])", - (match lookup mods x_opt name act.at with + (match lookup_export env x_opt name act.at with | ExternFuncT dt -> let FuncT (_, out) as ft = as_func_str_type (expand_def_type dt) in if is_js_func_type ft then None else - Some (of_wrapper mods x_opt name (invoke ft vs), out) + Some (of_wrapper env x_opt name (invoke ft vs), out) | _ -> None ) | Get (x_opt, name) -> - "get(" ^ of_var_opt mods x_opt ^ ", " ^ of_name name ^ ")", - (match lookup mods x_opt name act.at with + "get(" ^ of_inst_opt env x_opt ^ ", " ^ of_name name ^ ")", + (match lookup_export env x_opt name act.at with | ExternGlobalT gt when not (is_js_global_type gt) -> let GlobalT (_, t) = gt in - Some (of_wrapper mods x_opt name (get gt), [t]) + Some (of_wrapper env x_opt name (get gt), [t]) | _ -> None ) -let of_assertion' mods act name args wrapper_opt = - let act_js, act_wrapper_opt = of_action mods act in +let of_assertion' env act name args wrapper_opt = + let act_js, act_wrapper_opt = of_action env act in let js = name ^ "(() => " ^ act_js ^ String.concat ", " ("" :: args) ^ ")" in match act_wrapper_opt with | None -> js ^ ";" @@ -614,7 +643,7 @@ let of_assertion' mods act name args wrapper_opt = | Some wrapper -> "run", wrapper in run_name ^ "(() => " ^ act_wrapper (wrapper out) act.at ^ "); // " ^ js -let of_assertion mods ass = +let of_assertion env ass = match ass.it with | AssertMalformed (def, _) -> "assert_malformed(" ^ of_definition def ^ ");" @@ -624,21 +653,21 @@ let of_assertion mods ass = "assert_invalid(" ^ of_definition def ^ ");" | AssertInvalidCustom (def, _) -> "assert_invalid_custom(" ^ of_definition def ^ ");" - | AssertUnlinkable (def, _) -> - "assert_unlinkable(" ^ of_definition def ^ ");" - | AssertUninstantiable (def, _) -> - "assert_uninstantiable(" ^ of_definition def ^ ");" + | AssertUnlinkable (x_opt, _) -> + "assert_unlinkable(" ^ of_instance env x_opt ^ ");" + | AssertUninstantiable (x_opt, _) -> + "assert_uninstantiable(" ^ of_instance env x_opt ^ ");" | AssertReturn (act, ress) -> - of_assertion' mods act "assert_return" (List.map of_result ress) + of_assertion' env act "assert_return" (List.map of_result ress) (Some (assert_return ress)) | AssertTrap (act, _) -> - of_assertion' mods act "assert_trap" [] None + of_assertion' env act "assert_trap" [] None | AssertExhaustion (act, _) -> - of_assertion' mods act "assert_exhaustion" [] None + of_assertion' env act "assert_exhaustion" [] None | AssertException act -> - of_assertion' mods act "assert_exception" [] None + of_assertion' env act "assert_exception" [] None -let of_command mods cmd = +let of_command env cmd = "\n// " ^ Filename.basename cmd.at.left.file ^ ":" ^ string_of_int cmd.at.left.line ^ "\n" ^ match cmd.it with @@ -649,18 +678,24 @@ let of_command mods cmd = | Encoded (name, bs) -> Decode.decode name bs.it | Quoted (_, s) -> unquote (snd (Parse.Module.parse_string ~offset:s.at s.it)) - in bind mods x_opt (unquote def); - "let " ^ current_var mods ^ " = instance(" ^ of_definition def ^ ");\n" ^ + in bind_mod env x_opt (unquote def); + "let " ^ current_mod env ^ " = " ^ of_definition def ^ ";\n" ^ (if x_opt = None then "" else - "let " ^ of_var_opt mods x_opt ^ " = " ^ current_var mods ^ ";\n") + "let " ^ of_mod_opt env x_opt ^ " = " ^ current_mod env ^ ";\n") + | Instance (x1_opt, x2_opt) -> + let exports = find_mod env x2_opt cmd.at in + bind_inst env x1_opt exports; + "let " ^ current_inst env ^ " = instance(" ^ of_mod_opt env x2_opt ^ ");\n" ^ + (if x1_opt = None then "" else + "let " ^ of_inst_opt env x1_opt ^ " = " ^ current_inst env ^ ";\n") | Register (name, x_opt) -> - "register(" ^ of_name name ^ ", " ^ of_var_opt mods x_opt ^ ")\n" + "register(" ^ of_name name ^ ", " ^ of_inst_opt env x_opt ^ ")\n" | Action act -> - of_assertion' mods act "run" [] None ^ "\n" + of_assertion' env act "run" [] None ^ "\n" | Assertion ass -> - of_assertion mods ass ^ "\n" + of_assertion env ass ^ "\n" | Meta _ -> assert false let of_script scr = (if !Flags.harness then harness else "") ^ - String.concat "" (List.map (of_command (modules ())) scr) + String.concat "" (List.map (of_command (env ())) scr) diff --git a/interpreter/script/run.ml b/interpreter/script/run.ml index 1ff6a60b3..81496b632 100644 --- a/interpreter/script/run.ml +++ b/interpreter/script/run.ml @@ -308,7 +308,7 @@ let lookup category map x_opt at = let lookup_script = lookup "script" scripts let lookup_module = lookup "module" modules -let lookup_instance = lookup "module" instances +let lookup_instance = lookup "module instance" instances let lookup_registry module_name item_name _t = match Instance.export (Map.find module_name !registry) item_name with @@ -474,9 +474,9 @@ let run_assertion ass = | _ -> Assert.error ass.at "expected custom validation error" ) - | AssertUnlinkable (def, re) -> + | AssertUnlinkable (x_opt, re) -> trace "Asserting unlinkable..."; - let m, cs = run_definition def in + let m, cs = lookup_module x_opt ass.at in if not !Flags.unchecked then Valid.check_module_with_custom (m, cs); (match let imports = Import.link m in @@ -487,9 +487,9 @@ let run_assertion ass = | _ -> Assert.error ass.at "expected linking error" ) - | AssertUninstantiable (def, re) -> + | AssertUninstantiable (x_opt, re) -> trace "Asserting trap..."; - let m, cs = run_definition def in + let m, cs = lookup_module x_opt ass.at in if not !Flags.unchecked then Valid.check_module_with_custom (m, cs); (match let imports = Import.link m in @@ -542,12 +542,16 @@ let rec run_command cmd = end end; bind "module" modules x_opt (m, cs); - bind "script" scripts x_opt [cmd]; + bind "script" scripts x_opt [cmd] + + | Instance (x1_opt, x2_opt) -> + quote := cmd :: !quote; + let m, cs = lookup_module x2_opt cmd.at in if not !Flags.dry then begin trace "Initializing..."; let imports = Import.link m in let inst = Eval.init m imports in - bind "instance" instances x_opt inst + bind "instance" instances x1_opt inst end | Register (name, x_opt) -> diff --git a/interpreter/script/script.ml b/interpreter/script/script.ml index df4b74af0..315905e31 100644 --- a/interpreter/script/script.ml +++ b/interpreter/script/script.ml @@ -44,8 +44,8 @@ and assertion' = | AssertMalformedCustom of definition * string | AssertInvalid of definition * string | AssertInvalidCustom of definition * string - | AssertUnlinkable of definition * string - | AssertUninstantiable of definition * string + | AssertUnlinkable of var option * string + | AssertUninstantiable of var option * string | AssertReturn of action * result list | AssertException of action | AssertTrap of action * string @@ -54,6 +54,7 @@ and assertion' = type command = command' Source.phrase and command' = | Module of var option * definition + | Instance of var option * var option | Register of Ast.name * var option | Action of action | Assertion of assertion diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index 0caeb47bb..3d5ac0ba6 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -735,21 +735,25 @@ let custom m mnode (module S : Custom.Section) = S.Handler.arrange m mnode S.it +let var x = + if String.for_all (fun c -> Lib.Char.is_alphanum_ascii c || c = '_') x.it then + "$" ^ x.it + else + "$" ^ name (Utf8.decode x.it) + let var_opt = function | None -> "" - | Some x when - String.for_all (fun c -> Lib.Char.is_alphanum_ascii c || c = '_') x.it -> - " $" ^ x.it - | Some x -> " $" ^ name (Utf8.decode x.it) + | Some x -> " " ^ var x -let module_with_var_opt x_opt (m, cs) = +let module_with_var_opt isdef x_opt (m, cs) = let fx = ref 0 in let tx = ref 0 in let mx = ref 0 in let tgx = ref 0 in let gx = ref 0 in let imports = list (import fx tx mx tgx gx) m.it.imports in - let ret = Node ("module" ^ var_opt x_opt, + let head = if isdef then "module definition" else "module" in + let ret = Node (head ^ var_opt x_opt, List.rev (fst (List.fold_left type_ ([], 0) m.it.types)) @ imports @ listi (table !tx) m.it.tables @ @@ -765,13 +769,15 @@ let module_with_var_opt x_opt (m, cs) = List.fold_left (custom m) ret cs -let binary_module_with_var_opt x_opt bs = - Node ("module" ^ var_opt x_opt ^ " binary", break_bytes bs) +let binary_module_with_var_opt isdef x_opt bs = + let head = if isdef then "module definition" else "module" in + Node (head ^ var_opt x_opt ^ " binary", break_bytes bs) -let quoted_module_with_var_opt x_opt s = - Node ("module" ^ var_opt x_opt ^ " quote", break_string s) +let quoted_module_with_var_opt isdef x_opt s = + let head = if isdef then "module definition" else "module" in + Node (head ^ var_opt x_opt ^ " quote", break_string s) -let module_with_custom = module_with_var_opt None +let module_with_custom = module_with_var_opt false None let module_ m = module_with_custom (m, []) @@ -792,7 +798,7 @@ let literal mode lit = | Vec v -> Node (vec_constop v ^ " " ^ vec mode v, []) | Ref r -> ref_ r -let definition mode x_opt def = +let definition mode isdef x_opt def = try match mode with | `Textual -> @@ -802,7 +808,7 @@ let definition mode x_opt def = | Encoded (name, bs) -> Decode.decode_with_custom name bs.it | Quoted (_, s) -> unquote (snd (Parse.Module.parse_string ~offset:s.at s.it)) - in module_with_var_opt x_opt (unquote def) + in module_with_var_opt isdef x_opt (unquote def) | `Binary -> let rec unquote def = match def.it with @@ -811,14 +817,14 @@ let definition mode x_opt def = Encode.encode_with_custom (Decode.decode_with_custom name bs.it) | Quoted (_, s) -> unquote (snd (Parse.Module.parse_string ~offset:s.at s.it)) - in binary_module_with_var_opt x_opt (unquote def) + in binary_module_with_var_opt isdef x_opt (unquote def) | `Original -> match def.it with - | Textual (m, cs) -> module_with_var_opt x_opt (m, cs) - | Encoded (_, bs) -> binary_module_with_var_opt x_opt bs.it - | Quoted (_, s) -> quoted_module_with_var_opt x_opt s.it + | Textual (m, cs) -> module_with_var_opt isdef x_opt (m, cs) + | Encoded (_, bs) -> binary_module_with_var_opt isdef x_opt bs.it + | Quoted (_, s) -> quoted_module_with_var_opt isdef x_opt s.it with Parse.Syntax _ -> - quoted_module_with_var_opt x_opt "" + quoted_module_with_var_opt isdef x_opt "" let access x_opt n = String.concat " " [var_opt x_opt; name n] @@ -869,28 +875,31 @@ let result mode res = | VecResult vp -> vec_pat mode vp | RefResult rp -> ref_pat rp +let instance (x1_opt, x2_opt) = + Node ("module instance" ^ var_opt x1_opt ^ var_opt x2_opt, []) + let assertion mode ass = match ass.it with | AssertMalformed (def, re) -> (match mode, def.it with | `Binary, Quoted _ -> [] | _ -> - [Node ("assert_malformed", [definition `Original None def; Atom (string re)])] + [Node ("assert_malformed", [definition `Original false None def; Atom (string re)])] ) | AssertMalformedCustom (def, re) -> (match mode, def.it with | `Binary, Quoted _ -> [] | _ -> - [Node ("assert_malformed_custom", [definition `Original None def; Atom (string re)])] + [Node ("assert_malformed_custom", [definition `Original false None def; Atom (string re)])] ) | AssertInvalid (def, re) -> - [Node ("assert_invalid", [definition mode None def; Atom (string re)])] + [Node ("assert_invalid", [definition mode false None def; Atom (string re)])] | AssertInvalidCustom (def, re) -> - [Node ("assert_invalid_custom", [definition mode None def; Atom (string re)])] - | AssertUnlinkable (def, re) -> - [Node ("assert_unlinkable", [definition mode None def; Atom (string re)])] - | AssertUninstantiable (def, re) -> - [Node ("assert_trap", [definition mode None def; Atom (string re)])] + [Node ("assert_invalid_custom", [definition mode false None def; Atom (string re)])] + | AssertUnlinkable (x_opt, re) -> + [Node ("assert_unlinkable", [instance (None, x_opt); Atom (string re)])] + | AssertUninstantiable (x_opt, re) -> + [Node ("assert_trap", [instance (None, x_opt); Atom (string re)])] | AssertReturn (act, results) -> [Node ("assert_return", action mode act :: List.map (result mode) results)] | AssertTrap (act, re) -> @@ -902,7 +911,8 @@ let assertion mode ass = let command mode cmd = match cmd.it with - | Module (x_opt, def) -> [definition mode x_opt def] + | Module (x_opt, def) -> [definition mode true x_opt def] + | Instance (x1_opt, x2_opt) -> [instance (x1_opt, x2_opt)] | Register (n, x_opt) -> [Node ("register " ^ name n ^ var_opt x_opt, [])] | Action act -> [action mode act] | Assertion ass -> assertion mode ass diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index f1d7105f9..5b95a1249 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -763,6 +763,8 @@ rule token = parse | "module" -> MODULE | "binary" -> BIN | "quote" -> QUOTE + | "definition" -> DEFINITION + | "instance" -> INSTANCE | "script" -> SCRIPT | "register" -> REGISTER diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 6527bbe94..536685b58 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -10,6 +10,8 @@ open Script let error at msg = raise (Parse_error.Syntax (at, msg)) +let thd (_, _, x) = x + (* Position handling *) @@ -366,7 +368,7 @@ let parse_annots (m : module_) : Custom.section list = %token Ast.instr'> VEC_EXTRACT VEC_REPLACE %token FUNC START TYPE PARAM RESULT LOCAL GLOBAL %token TABLE ELEM MEMORY TAG DATA DECLARE OFFSET ITEM IMPORT EXPORT -%token MODULE BIN QUOTE +%token MODULE BIN QUOTE DEFINITION INSTANCE %token SCRIPT REGISTER INVOKE GET %token ASSERT_MALFORMED ASSERT_INVALID ASSERT_UNLINKABLE %token ASSERT_RETURN ASSERT_TRAP ASSERT_EXCEPTION ASSERT_EXHAUSTION @@ -1447,49 +1449,82 @@ inline_module1 : /* Sugar */ script_var : | VAR { var $1 $sloc } /* Sugar */ +instance_var : + | VAR { var $1 $sloc } /* Sugar */ + +definition_opt : + | DEFINITION { true } + | /* empty */ { false } + script_module : - | module_ { $1 } - | LPAR MODULE option(module_var) BIN string_list RPAR - { let s = $5 @@ $loc($5) in - $3, Encoded ("binary:" ^ string_of_pos (at $sloc).left, s) @@ $sloc } - | LPAR MODULE option(module_var) QUOTE string_list RPAR - { let s = $5 @@ $loc($5) in - $3, Quoted ("quote:" ^ string_of_pos (at $sloc).left, s) @@ $sloc } + | LPAR MODULE definition_opt option(module_var) module_fields RPAR + { let m = $5 (empty_context ()) () () @@ $sloc in + $3, $4, Textual (m, parse_annots m) @@ $sloc } + | LPAR MODULE definition_opt option(module_var) BIN string_list RPAR + { let s = $6 @@ $loc($5) in + $3, $4, Encoded ("binary:" ^ string_of_pos (at $sloc).left, s) @@ $sloc } + | LPAR MODULE definition_opt option(module_var) QUOTE string_list RPAR + { let s = $6 @@ $loc($5) in + $3, $4, Quoted ("quote:" ^ string_of_pos (at $sloc).left, s) @@ $sloc } + +script_instance : + | instance { [], $1 } + | script_module /* sugar */ + { let isdef, var_opt, m = $1 in + if isdef then error (at $sloc) "misplaced module definition"; + [Module (None, m) @@ $sloc], (var_opt, None) } + +instance : + | LPAR MODULE INSTANCE instance_var module_var RPAR + { Some $4, Some $5 } + | LPAR MODULE INSTANCE module_var RPAR + { None, Some $4 } + | LPAR MODULE INSTANCE RPAR + { None, None } action : - | LPAR INVOKE option(module_var) name list(literal) RPAR + | LPAR INVOKE option(instance_var) name list(literal) RPAR { Invoke ($3, $4, $5) @@ $sloc } - | LPAR GET option(module_var) name RPAR + | LPAR GET option(instance_var) name RPAR { Get ($3, $4) @@ $sloc } assertion : | LPAR ASSERT_MALFORMED script_module STRING RPAR - { AssertMalformed (snd $3, $4) @@ $sloc } + { [], AssertMalformed (thd $3, $4) @@ $sloc } | LPAR ASSERT_INVALID script_module STRING RPAR - { AssertInvalid (snd $3, $4) @@ $sloc } + { [], AssertInvalid (thd $3, $4) @@ $sloc } | LPAR ASSERT_MALFORMED_CUSTOM script_module STRING RPAR - { AssertMalformedCustom (snd $3, $4) @@ $sloc } + { [], AssertMalformedCustom (thd $3, $4) @@ $sloc } | LPAR ASSERT_INVALID_CUSTOM script_module STRING RPAR - { AssertInvalidCustom (snd $3, $4) @@ $sloc } - | LPAR ASSERT_UNLINKABLE script_module STRING RPAR - { AssertUnlinkable (snd $3, $4) @@ $sloc } - | LPAR ASSERT_TRAP script_module STRING RPAR - { AssertUninstantiable (snd $3, $4) @@ $sloc } - | LPAR ASSERT_RETURN action list(result) RPAR { AssertReturn ($3, $4) @@ $sloc } + { [], AssertInvalidCustom (thd $3, $4) @@ $sloc } + | LPAR ASSERT_UNLINKABLE script_instance STRING RPAR + { fst $3, AssertUnlinkable (snd (snd $3), $4) @@ $sloc } + | LPAR ASSERT_TRAP script_instance STRING RPAR + { fst $3, AssertUninstantiable (snd (snd $3), $4) @@ $sloc } + | LPAR ASSERT_RETURN action list(result) RPAR + { [], AssertReturn ($3, $4) @@ $sloc } | LPAR ASSERT_EXCEPTION action RPAR - { AssertException $3 @@ $sloc } - | LPAR ASSERT_TRAP action STRING RPAR { AssertTrap ($3, $4) @@ $sloc } - | LPAR ASSERT_EXHAUSTION action STRING RPAR { AssertExhaustion ($3, $4) @@ $sloc } + { [], AssertException $3 @@ $sloc } + | LPAR ASSERT_TRAP action STRING RPAR + { [], AssertTrap ($3, $4) @@ $sloc } + | LPAR ASSERT_EXHAUSTION action STRING RPAR + { [], AssertExhaustion ($3, $4) @@ $sloc } cmd : - | action { Action $1 @@ $sloc } - | assertion { Assertion $1 @@ $sloc } - | script_module { Module (fst $1, snd $1) @@ $sloc } - | LPAR REGISTER name option(module_var) RPAR { Register ($3, $4) @@ $sloc } - | meta { Meta $1 @@ $sloc } + | action { [Action $1 @@ $sloc] } + | assertion { fst $1 @ [Assertion (snd $1) @@ $sloc] } + | script_module + { let isdef, var_opt, m = $1 in + if isdef then + [Module (var_opt, m) @@ $sloc] + else (* sugar *) + [Module (var_opt, m) @@ $sloc; Instance (var_opt, var_opt) @@ $sloc] } + | instance { [Instance (fst $1, snd $1) @@ $sloc] } + | LPAR REGISTER name option(instance_var) RPAR { [Register ($3, $4) @@ $sloc] } + | meta { [Meta $1 @@ $sloc] } meta : - | LPAR SCRIPT option(script_var) list(cmd) RPAR { Script ($3, $4) @@ $sloc } + | LPAR SCRIPT option(script_var) list(cmd) RPAR { Script ($3, List.concat $4) @@ $sloc } | LPAR INPUT option(script_var) STRING RPAR { Input ($3, $4) @@ $sloc } | LPAR OUTPUT option(script_var) STRING RPAR { Output ($3, Some $4) @@ $sloc } | LPAR OUTPUT option(script_var) RPAR { Output ($3, None) @@ $sloc } @@ -1534,11 +1569,11 @@ result : (Value.V128 ($3, List.map (fun lit -> lit $3) $4))) @@ $sloc } script : - | list(cmd) EOF { $1 } + | list(cmd) EOF { List.concat $1 } | inline_module1 EOF { [Module (None, $1) @@ $sloc] } /* Sugar */ script1 : - | cmd { [$1] } + | cmd { $1 } module1 : | module_ EOF { $1 } diff --git a/proposals/annotations/Overview.md b/proposals/annotations/Overview.md index dbd184206..bd9940405 100644 --- a/proposals/annotations/Overview.md +++ b/proposals/annotations/Overview.md @@ -93,7 +93,7 @@ Expressing generic custom sections (cf. https://gist.github.com/binji/d1cfff7faa Expressing names ```wasm (module (@name "Gümüsü") - (func $lambda (@name "λ") (param $x (@name "α βγ δ") i32) (result i32) (get_local $x)) + (func $lambda (@name "λ") (param $x (@name "α βγ δ") i32) (result i32) (local.get $x)) ) ``` diff --git a/test/core/binary.wast b/test/core/binary.wast index eec70f39c..38813935d 100644 --- a/test/core/binary.wast +++ b/test/core/binary.wast @@ -258,27 +258,47 @@ "\0a\01\00" ;; Code section with 0 functions ) -;; Fewer passive segments than datacount +;; Fewer passive segments than data count (assert_malformed (module binary "\00asm" "\01\00\00\00" - "\0c\01\03" ;; Datacount section with value "3" - "\0b\05\02" ;; Data section with two entries - "\01\00" ;; Passive data section - "\01\00") ;; Passive data section - "data count and data section have inconsistent lengths") + "\0c\01\03" ;; Data count section with value 3 + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00" ;; Passive data section + ) + "data count and data section have inconsistent lengths" +) -;; More passive segments than datacount +;; More passive segments than data count (assert_malformed (module binary "\00asm" "\01\00\00\00" - "\0c\01\01" ;; Datacount section with value "1" - "\0b\05\02" ;; Data section with two entries - "\01\00" ;; Passive data section - "\01\00") ;; Passive data section - "data count and data section have inconsistent lengths") + "\0c\01\01" ;; Data count section with value 1 + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00" ;; Passive data section + ) + "data count and data section have inconsistent lengths" +) + +;; Non-zero data count section without data section +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01\00\01" ;; Memory section with one entry + "\0c\01\01" ;; Data count section with value 1 + ) + "data count and data section have inconsistent lengths" +) + +;; Zero data count section without data section +(module binary + "\00asm" "\01\00\00\00" + "\0c\01\00" ;; Data count section with value 0 +) -;; memory.init requires a datacount section +;; memory.init requires a data count section (assert_malformed (module binary "\00asm" "\01\00\00\00" @@ -298,9 +318,10 @@ "\0b\03\01\01\00" ;; Data section ) ;; end - "data count section required") + "data count section required" +) -;; data.drop requires a datacount section +;; data.drop requires a data count section (assert_malformed (module binary "\00asm" "\01\00\00\00" @@ -317,7 +338,8 @@ "\0b\03\01\01\00" ;; Data section ) ;; end - "data count section required") + "data count section required" +) ;; passive element segment containing illegal opcode (assert_malformed @@ -342,8 +364,10 @@ ;; function 0 "\02\00" - "\0b") ;; end - "illegal opcode") + "\0b" ;; end + ) + "illegal opcode" +) ;; passive element segment containing type other than funcref (assert_malformed @@ -368,8 +392,10 @@ ;; function 0 "\02\00" - "\0b") ;; end - "malformed reference type") + "\0b" ;; end + ) + "malformed reference type" +) ;; passive element segment containing opcode ref.func (module binary @@ -393,7 +419,8 @@ ;; function 0 "\02\00" - "\0b") ;; end + "\0b" ;; end +) ;; passive element segment containing opcode ref.null (module binary @@ -417,7 +444,8 @@ ;; function 0 "\02\00" - "\0b") ;; end + "\0b" ;; end +) ;; Type count can be zero @@ -982,8 +1010,8 @@ (assert_malformed (module binary "\00asm" "\01\00\00\00" - "\0c\01\01" ;; Datacount section with value "1" - "\0c\01\01" ;; Datacount section with value "1" + "\0c\01\01" ;; Data count section with value "1" + "\0c\01\01" ;; Data count section with value "1" ) "unexpected content after last section" ) @@ -1154,18 +1182,18 @@ (assert_malformed (module binary "\00asm" "\01\00\00\00" - "\0c\01\01" ;; Datacount section with value "1" + "\0c\01\01" ;; Data count section with value "1" "\09\01\00" ;; Element section with zero entries ) "unexpected content after last section" ) -;; Datacount section out of order +;; Data count section out of order (assert_malformed (module binary "\00asm" "\01\00\00\00" "\0a\01\00" ;; Code section with zero entries - "\0c\01\01" ;; Datacount section with value "1" + "\0c\01\01" ;; Data count section with value "1" ) "unexpected content after last section" ) diff --git a/test/core/instance.wast b/test/core/instance.wast new file mode 100644 index 000000000..b88025d69 --- /dev/null +++ b/test/core/instance.wast @@ -0,0 +1,170 @@ +;; Instantiation is generative + +(module definition $M + (global (export "glob") (mut i32) (i32.const 0)) + (table (export "tab") 10 funcref (ref.null func)) + (memory (export "mem") 1) + (tag (export "tag")) +) + +(module instance $I1 $M) +(module instance $I2 $M) +(register "I1" $I1) +(register "I2" $I2) + +(module + (import "I1" "glob" (global $glob1 (mut i32))) + (import "I2" "glob" (global $glob2 (mut i32))) + (import "I1" "tab" (table $tab1 10 funcref)) + (import "I2" "tab" (table $tab2 10 funcref)) + (import "I1" "mem" (memory $mem1 1)) + (import "I2" "mem" (memory $mem2 1)) + (import "I1" "tag" (tag $tag1)) + (import "I2" "tag" (tag $tag2)) + + (func $f) + (elem declare func $f) + + (func (export "glob") (result i32) + (global.set $glob1 (i32.const 1)) + (global.get $glob2) + ) + (func (export "tab") (result funcref) + (table.set $tab1 (i32.const 0) (ref.func $f)) + (table.get $tab2 (i32.const 0)) + ) + (func (export "mem") (result i32) + (i32.store $mem1 (i32.const 0) (i32.const 1)) + (i32.load $mem2 (i32.const 0)) + ) + (func (export "tag") (result i32) + (block $on_tag1 + (block $on_other + (try_table (catch $tag1 $on_tag1) (catch_all $on_other) + (throw $tag2) + ) + (unreachable) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(assert_return (invoke "glob") (i32.const 0)) +(assert_return (invoke "tab") (ref.null)) +(assert_return (invoke "mem") (i32.const 0)) +(assert_return (invoke "tag") (i32.const 0)) + + +;; Import is not generative + +(module + (import "I1" "glob" (global $glob1 (mut i32))) + (import "I1" "glob" (global $glob2 (mut i32))) + (import "I1" "tab" (table $tab1 10 funcref)) + (import "I1" "tab" (table $tab2 10 funcref)) + (import "I1" "mem" (memory $mem1 1)) + (import "I1" "mem" (memory $mem2 1)) + (import "I1" "tag" (tag $tag1)) + (import "I1" "tag" (tag $tag2)) + + (func $f) + (elem declare func $f) + + (func (export "glob") (result i32) + (global.set $glob1 (i32.const 1)) + (global.get $glob2) + ) + (func (export "tab") (result funcref) + (table.set $tab1 (i32.const 0) (ref.func $f)) + (table.get $tab2 (i32.const 0)) + ) + (func (export "mem") (result i32) + (i32.store $mem1 (i32.const 0) (i32.const 1)) + (i32.load $mem2 (i32.const 0)) + ) + (func (export "tag") (result i32) + (block $on_tag1 + (block $on_other + (try_table (catch $tag1 $on_tag1) (catch_all $on_other) + (throw $tag2) + ) + (unreachable) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(assert_return (invoke "glob") (i32.const 1)) +(assert_return (invoke "tab") (ref.func)) +(assert_return (invoke "mem") (i32.const 1)) +(assert_return (invoke "tag") (i32.const 1)) + + +;; Export is not generative + +(module definition $N + (global $glob (mut i32) (i32.const 0)) + (table $tab 10 funcref (ref.null func)) + (memory $mem 1) + (tag $tag) + + (export "glob1" (global $glob)) + (export "glob2" (global $glob)) + (export "tab1" (table $tab)) + (export "tab2" (table $tab)) + (export "mem1" (memory $mem)) + (export "mem2" (memory $mem)) + (export "tag1" (tag $tag)) + (export "tag2" (tag $tag)) +) + +(module instance $I $N) +(register "I" $I) + +(module + (import "I" "glob1" (global $glob1 (mut i32))) + (import "I" "glob2" (global $glob2 (mut i32))) + (import "I" "tab1" (table $tab1 10 funcref)) + (import "I" "tab2" (table $tab2 10 funcref)) + (import "I" "mem1" (memory $mem1 1)) + (import "I" "mem2" (memory $mem2 1)) + (import "I" "tag1" (tag $tag1)) + (import "I" "tag2" (tag $tag2)) + + (func $f) + (elem declare func $f) + + (func (export "glob") (result i32) + (global.set $glob1 (i32.const 1)) + (global.get $glob2) + ) + (func (export "tab") (result funcref) + (table.set $tab1 (i32.const 0) (ref.func $f)) + (table.get $tab2 (i32.const 0)) + ) + (func (export "mem") (result i32) + (i32.store $mem1 (i32.const 0) (i32.const 1)) + (i32.load $mem2 (i32.const 0)) + ) + (func (export "tag") (result i32) + (block $on_tag1 + (block $on_other + (try_table (catch $tag1 $on_tag1) (catch_all $on_other) + (throw $tag2) + ) + (unreachable) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(assert_return (invoke "glob") (i32.const 1)) +(assert_return (invoke "tab") (ref.func)) +(assert_return (invoke "mem") (i32.const 1)) +(assert_return (invoke "tag") (i32.const 1)) diff --git a/test/harness/async_index.js b/test/harness/async_index.js index a0ed5f97d..5812f347f 100644 --- a/test/harness/async_index.js +++ b/test/harness/async_index.js @@ -293,12 +293,26 @@ function assert_return(action, ...expected) { // so there's no good way to test that it's a canonical NaN. assert_true(Number.isNaN(actual[i]), `expected NaN, observed ${actual[i]}.`); return; + case "ref.i31": + assert_true(typeof actual[i] === "number" && (actual[i] & 0x7fffffff) === actual[i], `expected Wasm i31, got ${actual[i]}`); + return; + case "ref.any": + case "ref.eq": + case "ref.struct": + case "ref.array": + // For now, JS can't distinguish exported Wasm GC values, + // so we only test for object. + assert_true(typeof actual[i] === "object", `expected Wasm GC object, got ${actual[i]}`); + return; case "ref.func": assert_true(typeof actual[i] === "function", `expected Wasm function, got ${actual[i]}`); return; case "ref.extern": assert_true(actual[i] !== null, `expected Wasm reference, got ${actual[i]}`); return; + case "ref.null": + assert_true(actual[i] === null, `expected Wasm null reference, got ${actual[i]}`); + return; default: assert_equals(actual[i], expected[i], loc); } diff --git a/test/harness/sync_index.js b/test/harness/sync_index.js index 259703208..069c97dff 100644 --- a/test/harness/sync_index.js +++ b/test/harness/sync_index.js @@ -374,12 +374,26 @@ function assert_return(action, ...expected) { // so there's no good way to test that it's a canonical NaN. assert_true(Number.isNaN(actual[i]), `expected NaN, observed ${actual[i]}.`); return; + case "ref.i31": + assert_true(typeof actual[i] === "number" && (actual[i] & 0x7fffffff) === actual[i], `expected Wasm i31, got ${actual[i]}`); + return; + case "ref.any": + case "ref.eq": + case "ref.struct": + case "ref.array": + // For now, JS can't distinguish exported Wasm GC values, + // so we only test for object. + assert_true(typeof actual[i] === "object", `expected Wasm GC object, got ${actual[i]}`); + return; case "ref.func": assert_true(typeof actual[i] === "function", `expected Wasm function, got ${actual[i]}`); return; case "ref.extern": assert_true(actual[i] !== null, `expected Wasm reference, got ${actual[i]}`); return; + case "ref.null": + assert_true(actual[i] === null, `expected Wasm null reference, got ${actual[i]}`); + return; default: assert_equals(actual[i], expected[i]); }