From 123ac59cb650e9537ce129bb2977243bcbe9a773 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Fri, 27 Oct 2023 20:59:25 +0200 Subject: [PATCH] [interpreter/tests] Switch to Option B' (#285) * Reintroduce exnref * Fix type_of * Simplify parser * Implement 1b * Change opcodes for catch/catch_all to avoid conflict * Put catch clauses first * Remove obsolete Delegating cases * Change exn type opcode to -0x17 * Switch to B' variant * [interpreter] Add boilerplate for ref.exn result patterns * [ci] Deactivate node run, since it can't handle try_table yet * Try -> TryTable in AST * [spec] Update spec for option B' (#283) * Deactivate Bikeshed * [spec/tests] Specification of legacy exceptions + tests (#284) * [legacy] Create specification doc for legacy exception handling * [test] Create infra for legacy tests --- .github/workflows/ci-interpreter.yml | 4 +- .github/workflows/ci-spec.yml | 5 +- document/Makefile | 2 +- document/core/appendix/changes.rst | 4 +- document/core/appendix/index-instructions.py | 14 +- document/core/appendix/index-rules.rst | 1 + document/core/appendix/index-types.rst | 4 +- document/core/appendix/properties.rst | 239 ++- document/core/binary/instructions.rst | 29 +- document/core/binary/types.rst | 3 +- document/core/exec/instructions.rst | 341 ++--- document/core/exec/modules.rst | 19 +- document/core/exec/runtime.rst | 170 +-- document/core/syntax/instructions.rst | 27 +- document/core/syntax/types.rst | 4 +- document/core/text/instructions.rst | 46 +- document/core/text/modules.rst | 1 + document/core/text/types.rst | 2 + document/core/util/macros.def | 34 +- document/core/valid/conventions.rst | 8 +- document/core/valid/instructions.rst | 162 +- document/core/valid/modules.rst | 2 +- document/index.html | 17 +- document/js-api/index.bs | 22 +- document/legacy/exceptions/.gitignore | 3 + document/legacy/exceptions/LICENSE | 50 + document/legacy/exceptions/Makefile | 358 +++++ document/legacy/exceptions/README.md | 25 + .../exceptions/appendix/index-instructions.py | 116 ++ document/legacy/exceptions/binary.rst | 31 + document/legacy/exceptions/conf.py | 498 ++++++ document/legacy/exceptions/exec.rst | 351 +++++ document/legacy/exceptions/index.rst | 22 + document/legacy/exceptions/intro.rst | 8 + document/legacy/exceptions/static/custom.css | 78 + .../legacy/exceptions/static/webassembly.png | Bin 0 -> 43955 bytes document/legacy/exceptions/syntax.rst | 38 + document/legacy/exceptions/text.rst | 37 + document/legacy/exceptions/util/macros.def | 1342 +++++++++++++++++ document/legacy/exceptions/util/mathdef.py | 122 ++ .../legacy/exceptions/util/pseudo-lexer.py | 32 + document/legacy/exceptions/valid.rst | 150 ++ interpreter/README.md | 15 +- interpreter/binary/decode.ml | 65 +- interpreter/binary/encode.ml | 28 +- interpreter/exec/eval.ml | 99 +- interpreter/script/js.ml | 1 + interpreter/script/run.ml | 1 + interpreter/syntax/ast.ml | 15 +- interpreter/syntax/free.ml | 18 +- interpreter/syntax/operators.ml | 10 +- interpreter/syntax/types.ml | 4 +- interpreter/syntax/values.ml | 11 +- interpreter/text/arrange.ml | 23 +- interpreter/text/lexer.mll | 18 +- interpreter/text/parser.mly | 153 +- interpreter/valid/valid.ml | 74 +- proposals/exception-handling/Exceptions.md | 387 +---- test/core/binary.wast | 2 +- test/core/ref_null.wast | 3 + test/core/throw.wast | 13 +- test/core/throw_ref.wast | 118 ++ test/core/try_table.wast | 372 +++++ test/{core => legacy/exceptions}/rethrow.wast | 0 test/legacy/exceptions/throw.wast | 51 + .../exceptions}/try_catch.wast | 0 .../exceptions}/try_delegate.wast | 0 test/legacy/run.py | 117 ++ 68 files changed, 4803 insertions(+), 1216 deletions(-) create mode 100644 document/legacy/exceptions/.gitignore create mode 100644 document/legacy/exceptions/LICENSE create mode 100644 document/legacy/exceptions/Makefile create mode 100644 document/legacy/exceptions/README.md create mode 100755 document/legacy/exceptions/appendix/index-instructions.py create mode 100644 document/legacy/exceptions/binary.rst create mode 100644 document/legacy/exceptions/conf.py create mode 100644 document/legacy/exceptions/exec.rst create mode 100644 document/legacy/exceptions/index.rst create mode 100644 document/legacy/exceptions/intro.rst create mode 100644 document/legacy/exceptions/static/custom.css create mode 100644 document/legacy/exceptions/static/webassembly.png create mode 100644 document/legacy/exceptions/syntax.rst create mode 100644 document/legacy/exceptions/text.rst create mode 100644 document/legacy/exceptions/util/macros.def create mode 100644 document/legacy/exceptions/util/mathdef.py create mode 100644 document/legacy/exceptions/util/pseudo-lexer.py create mode 100644 document/legacy/exceptions/valid.rst create mode 100644 test/core/throw_ref.wast create mode 100644 test/core/try_table.wast rename test/{core => legacy/exceptions}/rethrow.wast (100%) create mode 100644 test/legacy/exceptions/throw.wast rename test/{core => legacy/exceptions}/try_catch.wast (100%) rename test/{core => legacy/exceptions}/try_delegate.wast (100%) create mode 100755 test/legacy/run.py diff --git a/.github/workflows/ci-interpreter.yml b/.github/workflows/ci-interpreter.yml index 297abc7f..22efa999 100644 --- a/.github/workflows/ci-interpreter.yml +++ b/.github/workflows/ci-interpreter.yml @@ -31,4 +31,6 @@ jobs: - name: Build interpreter run: cd interpreter && opam exec make - name: Run tests - run: cd interpreter && opam exec make JS='node --experimental-wasm-return_call' ci + # Node can't handle the new instructions yet + # run: cd interpreter && opam exec make JS='node --experimental-wasm-return_call' ci + run: cd interpreter && opam exec make ci diff --git a/.github/workflows/ci-spec.yml b/.github/workflows/ci-spec.yml index 7610b33b..354dfbd4 100644 --- a/.github/workflows/ci-spec.yml +++ b/.github/workflows/ci-spec.yml @@ -32,8 +32,9 @@ jobs: run: pip install six && pip install sphinx==5.1.0 - name: Build main spec run: cd document/core && make main - - name: Run Bikeshed - run: cd document/core && make bikeshed + # Deactivate broken Bikeshed build for the time being + #- name: Run Bikeshed + # run: cd document/core && make bikeshed - name: Upload artifact uses: actions/upload-artifact@v2 with: diff --git a/document/Makefile b/document/Makefile index 875efc72..629d5f51 100644 --- a/document/Makefile +++ b/document/Makefile @@ -1,4 +1,4 @@ -DIRS = core js-api web-api +DIRS = core js-api web-api legacy/exceptions FILES = index.html BUILDDIR = _build diff --git a/document/core/appendix/changes.rst b/document/core/appendix/changes.rst index 16ea3ff7..db014c5f 100644 --- a/document/core/appendix/changes.rst +++ b/document/core/appendix/changes.rst @@ -147,9 +147,9 @@ Added tag definitions, imports, and exports, and instructions to throw and catch * Modules may :ref:`define `, :ref:`import `, and :ref:`export ` tags. -* New exception throwing :ref:`control instructions `: :math:`\THROW` and :math:`\RETHROW`. +* New :ref:`reference type ` |EXNREF|. -* New handler :ref:`control instructions `: :math:`(\TRY~\X{bt}~\instr_1^\ast~(\CATCH~x~\instr_2^\ast)^\ast~(\CATCHALL~\instr_3^\ast)^?\END)` and :math:`(\TRY~\X{bt}~\instr^\ast~\DELEGATE~l)`. +* New :ref:`control instructions `: |THROW|, |THROWREF|, and |TRYTABLE|. * New :ref:`tag section ` in binary format. diff --git a/document/core/appendix/index-instructions.py b/document/core/appendix/index-instructions.py index 4ab01739..e3a55f53 100755 --- a/document/core/appendix/index-instructions.py +++ b/document/core/appendix/index-instructions.py @@ -85,11 +85,11 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\LOOP~\X{bt}', r'\hex{03}', r'[t_1^\ast] \to [t_2^\ast]', r'valid-loop', r'exec-loop'), Instruction(r'\IF~\X{bt}', r'\hex{04}', r'[t_1^\ast~\I32] \to [t_2^\ast]', r'valid-if', r'exec-if'), Instruction(r'\ELSE', r'\hex{05}'), - Instruction(r'\TRY~\X{bt}', r'\hex{06}', r'[t_1^\ast] \to [t_2^\ast]', r'valid-try-catch', r'exec-try-catch', None, r'valid-try-delegate', r'exec-try-delegate'), - Instruction(r'\CATCH~x', r'\hex{07}'), + Instruction(None, r'\hex{06}'), + Instruction(None, r'\hex{07}'), Instruction(r'\THROW~x', r'\hex{08}', r'[t_1^\ast~t_x^\ast] \to [t_2^\ast]', r'valid-throw', r'exec-throw'), - Instruction(r'\RETHROW~n', r'\hex{09}', r'[t_1^\ast] \to [t_2^\ast]', r'valid-rethrow', r'exec-rethrow'), - Instruction(None, r'\hex{0A}'), + Instruction(None, r'\hex{09}'), + Instruction(r'\THROWREF', r'\hex{0A}', r'[t_1^\ast~(\REF~\NULL~\EXN)] \to [t_2^\ast]', r'valid-throw_ref', r'exec-throw_ref'), Instruction(r'\END', r'\hex{0B}'), Instruction(r'\BR~l', r'\hex{0C}', r'[t_1^\ast~t^\ast] \to [t_2^\ast]', r'valid-br', r'exec-br'), Instruction(r'\BRIF~l', r'\hex{0D}', r'[t^\ast~\I32] \to [t^\ast]', r'valid-br_if', r'exec-br_if'), @@ -103,14 +103,14 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(None, r'\hex{15}'), Instruction(None, r'\hex{16}'), Instruction(None, r'\hex{17}'), - Instruction(r'\DELEGATE~l', r'\hex{18}'), - Instruction(r'\CATCHALL', r'\hex{19}', None, r'valid-try-catch', r'exec-try-catch'), + Instruction(None, r'\hex{18}'), + Instruction(None, r'\hex{19}'), Instruction(r'\DROP', r'\hex{1A}', r'[t] \to []', r'valid-drop', r'exec-drop'), Instruction(r'\SELECT', r'\hex{1B}', r'[t~t~\I32] \to [t]', r'valid-select', r'exec-select'), Instruction(r'\SELECT~t', r'\hex{1C}', r'[t~t~\I32] \to [t]', r'valid-select', r'exec-select'), Instruction(None, r'\hex{1D}'), Instruction(None, r'\hex{1E}'), - Instruction(None, r'\hex{1F}'), + Instruction(r'\TRYTABLE~\X{bt}', r'\hex{1F}', r'[t_1^\ast] \to [t_2^\ast]', r'valid-try_table', r'exec-try_table'), Instruction(r'\LOCALGET~x', r'\hex{20}', r'[] \to [t]', r'valid-local.get', r'exec-local.get'), Instruction(r'\LOCALSET~x', r'\hex{21}', r'[t] \to []', r'valid-local.set', r'exec-local.set'), Instruction(r'\LOCALTEE~x', r'\hex{22}', r'[t] \to [t]', r'valid-local.tee', r'exec-local.tee'), diff --git a/document/core/appendix/index-rules.rst b/document/core/appendix/index-rules.rst index 5092be63..1cb3607e 100644 --- a/document/core/appendix/index-rules.rst +++ b/document/core/appendix/index-rules.rst @@ -23,6 +23,7 @@ Construct Judgement :ref:`External type ` :math:`\vdashexterntype \externtype \ok` :ref:`Instruction ` :math:`S;C \vdashinstr \instr : \stacktype` :ref:`Instruction sequence ` :math:`S;C \vdashinstrseq \instr^\ast : \stacktype` +:ref:`Catch clause ` :math:`C \vdashcatch \catch \ok` :ref:`Expression ` :math:`C \vdashexpr \expr : \resulttype` :ref:`Function ` :math:`C \vdashfunc \func : \functype` :ref:`Table ` :math:`C \vdashtable \table : \tabletype` diff --git a/document/core/appendix/index-types.rst b/document/core/appendix/index-types.rst index 5ec06bf4..e560796b 100644 --- a/document/core/appendix/index-types.rst +++ b/document/core/appendix/index-types.rst @@ -16,7 +16,9 @@ Category Constructor (reserved) :math:`\hex{7A}` .. :math:`\hex{71}` :ref:`Reference type ` |FUNCREF| :math:`\hex{70}` (-16 as |Bs7|) :ref:`Reference type ` |EXTERNREF| :math:`\hex{6F}` (-17 as |Bs7|) -(reserved) :math:`\hex{6E}` .. :math:`\hex{61}` +(reserved) :math:`\hex{6E}` .. :math:`\hex{6A}` +:ref:`Reference type ` |EXNREF| :math:`\hex{69}` (-23 as |Bs7|) +(reserved) :math:`\hex{68}` .. :math:`\hex{61}` :ref:`Function type ` :math:`[\valtype^\ast] \to [\valtype^\ast]` :math:`\hex{60}` (-32 as |Bs7|) (reserved) :math:`\hex{5F}` .. :math:`\hex{41}` :ref:`Result type ` :math:`[\epsilon]` :math:`\hex{40}` (-64 as |Bs7|) diff --git a/document/core/appendix/properties.rst b/document/core/appendix/properties.rst index 8ecba539..de9faa46 100644 --- a/document/core/appendix/properties.rst +++ b/document/core/appendix/properties.rst @@ -59,16 +59,12 @@ Results } -:ref:`Results ` :math:`\val^\ast~(\THROWadm~\tagaddr)` +:ref:`Results ` :math:`\XT[(\REFEXNADDR~a)~\THROWREF]` ..................................................................... -* The :ref:`external tag value ` :math:`\EVTAG~\tagaddr` must be :ref:`valid ` with :ref:`external tag type ` :math:`\ETTAG~[t^\ast]\to[]`. +* The value :math:`\REFEXNADDR~a` must be :ref:`valid `. -* For each :ref:`value ` :math:`\val_i` in :math:`\val^\ast` and corresponding :ref:`value type ` :math:`t_i` in :math:`t_i`: - - * The value :math:`\val_i` must be :ref:`valid ` with :ref:`value type ` :math:`t_i`. - -* Then the result is valid with :ref:`result type ` :math:`[{t'}^\ast]`, for any sequence :math:`{t'}^\ast` of :ref:`value types `. +* Then the result is valid with :ref:`result type ` :math:`[t^\ast]`, for any sequence :math:`{t'}^\ast` of :ref:`value types `. .. math:: @@ -77,7 +73,7 @@ Results \qquad (S \vdashval \val : t)^\ast }{ - S \vdashresult \val^\ast~(\THROWadm~\tagaddr) : [{t'}^\ast] + S \vdashresult \XT[(\REFEXNADDR~a)~\THROWREF] : [{t'}^\ast] } @@ -115,6 +111,8 @@ Module instances are classified by *module contexts*, which are regular :ref:`co * Each :ref:`data instance ` :math:`\datainst_i` in :math:`S.\SDATAS` must be :ref:`valid `. +* Each :ref:`exception instance ` :math:`\exninst_i` in :math:`S.\SEXNS` must be :ref:`valid `. + * Then the store is valid. .. math:: @@ -134,6 +132,8 @@ Module instances are classified by *module contexts*, which are regular :ref:`co (S \vdasheleminst \eleminst : \reftype)^\ast \qquad (S \vdashdatainst \datainst \ok)^\ast + \qquad + (S \vdashexninst \exninst \ok)^\ast \\ S = \{ \SFUNCS~\funcinst^\ast, @@ -143,7 +143,8 @@ Module instances are classified by *module contexts*, which are regular :ref:`co \\ \SGLOBALS~\globalinst^\ast, \SELEMS~\eleminst^\ast, - \SDATAS~\datainst^\ast \} + \SDATAS~\datainst^\ast, + \SEXNS~\exninst^\ast \} \end{array} }{ \vdashstore S \ok @@ -356,6 +357,34 @@ Module instances are classified by *module contexts*, which are regular :ref:`co } +.. index:: exception instance, tag, tag address +.. _valid-exninst: + +:ref:`Exception Instances ` :math:`\{ \EITAG~a, \EIFIELDS~\val^\ast \}` +....................................................................................... + +* The store entry :math:`S.\STAGS[a]` must exist. + +* Let :math:`[t^\ast] \toF [{t'}^\ast]` be the :ref:`tag type ` :math:`S.\STAGS[a].\TAGITYPE`. + +* The :ref:`result type ` :math:`[{t'}^\ast]` must be empty. + +* The sequence :math:`\val^ast` of :ref:`values ` must have the same length as the sequence :math:`t^\ast` of :ref:`value types `. + +* For each value :math:`\val_i` in :math:`\val^ast` and corresponding value type :math:`t_i` in :math:`t^\ast`, the value :math:`\val_i` must be valid with type :math:`t_i`. + +* Then the exception instance is valid. + +.. math:: + \frac{ + S.\STAGS[a] = \{\TAGITYPE = [t^\ast] \toF []\} + \qquad + (S \vdashval \val : t)^\ast + }{ + S \vdashexninst \{ \EITAG~a, \EIFIELDS~\val^\ast \} \ok + } + + .. index:: external type, export instance, name, external value .. _valid-exportinst: @@ -583,147 +612,20 @@ To that end, all previous typing judgements :math:`C \vdash \X{prop}` are genera } -.. index:: extern address - -:math:`\REFEXTERNADDR~\externaddr` -.................................. - -* The instruction is valid with type :math:`[] \to [\EXTERNREF]`. - -.. math:: - \frac{ - }{ - S; C \vdashadmininstr \REFEXTERNADDR~\externaddr : [] \to [\EXTERNREF] - } - - -.. index:: function address, extern value, extern type, function type - -:math:`\REFFUNCADDR~\funcaddr` -.............................. - -* The :ref:`external function value ` :math:`\EVFUNC~\funcaddr` must be :ref:`valid ` with :ref:`external function type ` :math:`\ETFUNC~\functype`. - -* Then the instruction is valid with type :math:`[] \to [\FUNCREF]`. - -.. math:: - \frac{ - S \vdashexternval \EVFUNC~\funcaddr : \ETFUNC~\functype - }{ - S; C \vdashadmininstr \REFFUNCADDR~\funcaddr : [] \to [\FUNCREF] - } - - -.. index:: throw, throw context, tag address - -:math:`\THROWadm~\tagaddr` -.......................... - -* The :ref:`external tag value ` :math:`\EVTAG~\tagaddr` must be :ref:`valid ` with :ref:`external tag type ` :math:`\ETTAG~[t^\ast]\to[]`. - -* Then the instruction is valid with type :math:`[t_1^\ast t^\ast] \to [t_2^\ast]` for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. - -.. math:: - \frac{ - S \vdashexternval \EVTAG~\tagaddr : \ETTAG~[t^\ast]\to[] - }{ - S; C \vdashadmininstr \THROWadm~\tagaddr : [t_1^\ast t^\ast] \to [t_2^\ast] - } - - -.. index:: handler, throw context - -:math:`\HANDLERadm_n\{(\tagaddr^?~\instr_1^\ast)^\ast\}~\instr_2^\ast~\END` -........................................................................... - -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. - -* Under context :math:`C'`, - the instruction sequence :math:`\instr_2^\ast` must be :ref:`valid ` with type :math:`[] \to [t_2^n]`. - -* Let :math:`C''` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`(\LCATCH~[t_2^n])` prepended to the |CLABELS| vector. - -* Under context :math:`C''`, - for every :math:`\tagaddr^?` and associated instruction sequence :math:`\instr_1^\ast`: - - * If :math:`\tagaddr^? = \epsilon`, then :math:`\instr_1^\ast` must be :ref:`valid ` with type :math:`[] \to [t_2^n]`. - - * Else: - - * The :ref:`external tag value ` :math:`\EVTAG~\tagaddr` must be :ref:`valid ` with some :ref:`external tag type ` :math:`\ETTAG~[t_1^\ast] \to []`. - - * The instruction sequence :math:`\instr_1^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^n]`. - -* Then the compound instruction is valid under context :math:`C'` with type :math:`[] \to [t_2^n]`. - -.. math:: - \frac{ - \begin{array}{@{}c@{}} - ((S \vdashexternval \EVTAG~\tagaddr : \ETTAG~[t_1^\ast]\to[])^? \\ - ~~S; C,\CLABELS\,(\LCATCH~[t_2^n]) \vdashinstrseq \instr_1^\ast : [(t_1^\ast)^?] \to [t_2^n])^\ast \\ - S; C,\CLABELS\,[t_2^n] \vdashinstrseq \instr_2^\ast : [] \to [t_2^n] \\ - \end{array} - }{ - S; C,\CLABELS\,[t_2^n] \vdashadmininstr \HANDLERadm_n\{(\tagaddr^?~{\instr_1}^\ast)^\ast\}~\instr_2^\ast~\END : [] \to [t_2^n] - } - - -.. index:: handler, throw context -.. _valid-handleradm: - -:math:`\HANDLERadm_n\{l\}~\instr^\ast~\END` -........................................... - -* The label :math:`C.\CLABELS[l]` must be defined in the context. - -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the label :math:`[t^\ast]` prepended to the |CLABELS| vector. - -* Under context :math:`C'`, - the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[]\to[t^n]`. - -* Then the compound instruction is valid under context :math:`C'` with type :math:`[] \to [t^n]`. - -.. math:: - \frac{ - S; C,\CLABELS\,[t^n] \vdashinstrseq \instr^\ast : [] \to [t^n] - \qquad - C.\CLABELS[l] = \LCATCH^?~[t_0^\ast] - }{ - S; C,\CLABELS\,[t^n] \vdashadmininstr \HANDLERadm_n\{l\}~\instr^\ast~\END : [] \to [t^n] - } - - -.. index:: caught, throw context - -:math:`\CAUGHTadm_n\{\tagaddr~\val^\ast\}~\instr^\ast~\END` -........................................................... - -* The :ref:`external tag value ` :math:`\EVTAG~\tagaddr` must be :ref:`valid ` with some :ref:`external tag type ` :math:`\ETTAG~[t_0^\ast] \to []`. - -* The :ref:`values ` :math:`\val^\ast` must be of type :math:`[t_0^\ast]`. - -* The label :math:`C.\CLABELS[0]` must be defined in the context. - -* Let :math:`(\LCATCH^?~[t^n])` be the :ref:`label type ` :math:`C.\CLABELS[0]`. - -* The |LCATCH| must not be present in the label type :math:`C.\CLABELS[0]`. +.. index:: value, reference -* Let :math:`C''` be the same :ref:`context ` as :math:`C`, but with the label type :math:`(\LCATCH~[t^n])` replacing the first element of the |CLABELS| vector. +:math:`\reff` +............. -* Under context :math:`C''`, - the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[] \to [t^n]`. +* The reference :math:`\reff` must be :ref:`valid ` with some type :math:`t`. -* Then the compound instruction is valid with type :math:`[] \to [t^n]`. +* Then the instruction is valid with type :math:`[] \to [t]`. .. math:: \frac{ - S \vdashexternval \EVTAG~\tagaddr : \ETTAG~[t_0^\ast]\to[] - \qquad - (val : t_0)^\ast - \qquad - S; C',\CLABELS\,(\LCATCH~[t^n]) \vdashinstrseq \instr^\ast : [] \to [t^n] + S \vdashval \reff : t }{ - S; C',\CLABELS\,[t^n] \vdashadmininstr \CAUGHTadm_n\{\tagaddr~\val^\ast\}~\instr^\ast~\END : [] \to [t^n] + S; C \vdashadmininstr \reff : [] \to [t] } @@ -751,7 +653,7 @@ To that end, all previous typing judgements :math:`C \vdash \X{prop}` are genera * The instruction sequence :math:`\instr_0^\ast` must be :ref:`valid ` with some type :math:`[t_1^n] \to [t_2^*]`. -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`[t_1^n]` prepended to the |CLABELS| vector. +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_1^n]` prepended to the |CLABELS| vector. * Under context :math:`C'`, the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[] \to [t_2^*]`. @@ -768,6 +670,29 @@ To that end, all previous typing judgements :math:`C \vdash \X{prop}` are genera } +.. index:: handler, throw context + +:math:`\HANDLER_n\{\catch^\ast\}~\instr^\ast~\END` +.................................................. + +* For every :ref:`catch clause ` :math:`\catch_i` in :math:`\catch^\ast`, :math:`\catch_i` must be :ref:`valid `. + +* The instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with some type :math:`[t_1^\ast] \to [t_2^\ast]`. + +* Then the compound instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`. + +.. math:: + \frac{ + \begin{array}{c} + (C \vdashcatch \catch \ok)^\ast + \qquad + S; C \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_2^\ast] \\ + \end{array} + }{ + S; C \vdashadmininstr \HANDLER_n\{\catch^\ast\}~\instr^\ast~\END : [t_1^\ast] \to [t_2^\ast] + } + + .. index:: frame, instruction, result type :math:`\FRAME_n\{F\}~\instr^\ast~\END` @@ -825,6 +750,8 @@ a store state :math:`S'` extends state :math:`S`, written :math:`S \extendsto S' * The length of :math:`S.\SDATAS` must not shrink. +* The length of :math:`S.\SEXNS` must not shrink. + * For each :ref:`function instance ` :math:`\funcinst_i` in the original :math:`S.\SFUNCS`, the new function instance must be an :ref:`extension ` of the old. * For each :ref:`table instance ` :math:`\tableinst_i` in the original :math:`S.\STABLES`, the new table instance must be an :ref:`extension ` of the old. @@ -835,9 +762,11 @@ a store state :math:`S'` extends state :math:`S`, written :math:`S \extendsto S' * For each :ref:`global instance ` :math:`\globalinst_i` in the original :math:`S.\SGLOBALS`, the new global instance must be an :ref:`extension ` of the old. -* For each :ref:`element instance ` :math:`\eleminst_i` in the original :math:`S.\SELEMS`, the new global instance must be an :ref:`extension ` of the old. +* For each :ref:`element instance ` :math:`\eleminst_i` in the original :math:`S.\SELEMS`, the new element instance must be an :ref:`extension ` of the old. -* For each :ref:`data instance ` :math:`\datainst_i` in the original :math:`S.\SDATAS`, the new global instance must be an :ref:`extension ` of the old. +* For each :ref:`data instance ` :math:`\datainst_i` in the original :math:`S.\SDATAS`, the new data instance must be an :ref:`extension ` of the old. + +* For each :ref:`exception instance ` :math:`\exninst_i` in the original :math:`S.\SEXNS`, the new exception instance must be an :ref:`extension ` of the old. .. math:: \frac{ @@ -863,6 +792,9 @@ a store state :math:`S'` extends state :math:`S`, written :math:`S \extendsto S' S_1.\SDATAS = \datainst_1^\ast & S_2.\SDATAS = {\datainst'_1}^\ast~\datainst_2^\ast & (\vdashdatainstextends \datainst_1 \extendsto \datainst'_1)^\ast \\ + S_1.\SEXNS = \exninst_1^\ast & + S_2.\SEXNS = {\exninst'_1}^\ast~\exninst_2^\ast & + (\vdashexninstextends \exninst_1 \extendsto \exninst'_1)^\ast \\ \end{array} }{ \vdashstoreextends S_1 \extendsto S_2 @@ -987,6 +919,21 @@ a store state :math:`S'` extends state :math:`S`, written :math:`S \extendsto S' } +.. index:: exception instance +.. _extend-exninst: + +:ref:`Exception Instance ` :math:`\exninst` +........................................................... + +* An exception instance must remain unchanged. + +.. math:: + \frac{ + }{ + \vdashexninstextends \exninst \extendsto \exninst + } + + .. index:: ! preservation, ! progress, soundness, configuration, thread, terminal configuration, instantiation, invocation, validity, module .. _soundness-statement: diff --git a/document/core/binary/instructions.rst b/document/core/binary/instructions.rst index b8ac093f..d26bfc48 100644 --- a/document/core/binary/instructions.rst +++ b/document/core/binary/instructions.rst @@ -21,7 +21,7 @@ The only exception are :ref:`structured control instructions ` have varying encodings. For structured instructions, the instruction sequences forming nested blocks are separated or terminated with explicit opcodes for |END|, |ELSE|, |CATCH|, |CATCHALL|, and |DELEGATE|. +:ref:`Control instructions ` have varying encodings. For structured instructions, the instruction sequences forming nested blocks are delimited with explicit opcodes for |END| and |ELSE|. :ref:`Block types ` are encoded in special compressed form, by either the byte :math:`\hex{40}` indicating the empty type, as a single :ref:`value type `, or as a :ref:`type index ` encoded as a positive :ref:`signed integer `. @@ -31,22 +31,23 @@ Control Instructions .. _binary-block: .. _binary-loop: .. _binary-if: -.. _binary-try: +.. _binary-try_table: .. _binary-throw: -.. _binary-rethrow: +.. _binary-throw_ref: .. _binary-br: .. _binary-br_if: .. _binary-br_table: .. _binary-return: .. _binary-call: .. _binary-call_indirect: +.. _binary-catch: .. math:: - \begin{array}{llcllll} + \begin{array}{@{}l@{}lclll@{}} \production{block type} & \Bblocktype &::=& \hex{40} &\Rightarrow& \epsilon \\ &&|& t{:}\Bvaltype &\Rightarrow& t \\ &&|& - x{:}\Bs33 &\Rightarrow& x & (\iff x \geq 0) \\ + x{:}\Bs33 &\Rightarrow& x \qquad\qquad (\iff x \geq 0) \\ \production{instruction} & \Binstr &::=& \hex{00} &\Rightarrow& \UNREACHABLE \\ &&|& \hex{01} &\Rightarrow& \NOP \\ &&|& @@ -59,22 +60,22 @@ Control Instructions \hex{04}~~\X{bt}{:}\Bblocktype~~(\X{in}_1{:}\Binstr)^\ast~~ \hex{05}~~(\X{in}_2{:}\Binstr)^\ast~~\hex{0B} &\Rightarrow& \IF~\X{bt}~\X{in}_1^\ast~\ELSE~\X{in}_2^\ast~\END \\ &&|& - \hex{06}~~\X{bt}{:}\Bblocktype~~(\X{in}_1{:}\Binstr)^\ast~~ - (\hex{07}~~x{:}\Btagidx~~(\X{in}_2{:}\Binstr)^\ast)^\ast~~ - (\hex{19}~~(\X{in}_3{:}\Binstr)^\ast)^?~~\hex{0B} - &\Rightarrow& \TRY~\X{bt}~\X{in}_1^\ast~(\CATCH~x~\X{in}_2^\ast)^\ast~ - (\CATCHALL~\X{in}_3^\ast)^?\END \\ &&|& - \hex{06}~~\X{bt}{:}\Bblocktype~~(\X{in}{:}\Binstr)^\ast~~\hex{18}~~l{:}\Blabelidx - &\Rightarrow& \TRY~\X{bt}~\X{in}^\ast~\DELEGATE~l \\ &&|& \hex{08}~~x{:}\Btagidx &\Rightarrow& \THROW~x \\ &&|& - \hex{09}~~l{:}\Blabelidx &\Rightarrow& \RETHROW~l \\ &&|& + \hex{0A} &\Rightarrow& \THROWREF \\ &&|& \hex{0C}~~l{:}\Blabelidx &\Rightarrow& \BR~l \\ &&|& \hex{0D}~~l{:}\Blabelidx &\Rightarrow& \BRIF~l \\ &&|& \hex{0E}~~l^\ast{:}\Bvec(\Blabelidx)~~l_N{:}\Blabelidx &\Rightarrow& \BRTABLE~l^\ast~l_N \\ &&|& \hex{0F} &\Rightarrow& \RETURN \\ &&|& \hex{10}~~x{:}\Bfuncidx &\Rightarrow& \CALL~x \\ &&|& - \hex{11}~~y{:}\Btypeidx~~x{:}\Btableidx &\Rightarrow& \CALLINDIRECT~x~y \\ + \hex{11}~~y{:}\Btypeidx~~x{:}\Btableidx &\Rightarrow& \CALLINDIRECT~x~y \\ &&|& + \hex{1F}~~\X{bt}{:}\Bblocktype~~c^\ast{:}\Bvec(\Bcatch)~~(\X{in}{:}\Binstr)^\ast~~\hex{0B} + &\Rightarrow& \TRYTABLE~\X{bt}~c^\ast~\X{in}^\ast~\END \\ + \production{catch clause} & \Bcatch &::=& + \hex{00}~~x{:}\Btagidx~~l{:}\Blabelidx &\Rightarrow& \CATCH~x~l \\ &&|& + \hex{01}~~x{:}\Btagidx~~l{:}\Blabelidx &\Rightarrow& \CATCHREF~x~l \\ &&|& + \hex{02}~~l{:}\Blabelidx &\Rightarrow& \CATCHALL~l \\ &&|& + \hex{03}~~l{:}\Blabelidx &\Rightarrow& \CATCHALLREF~l \\ \end{array} .. note:: diff --git a/document/core/binary/types.rst b/document/core/binary/types.rst index d9e1ca10..2eef0bee 100644 --- a/document/core/binary/types.rst +++ b/document/core/binary/types.rst @@ -58,7 +58,8 @@ Reference Types \begin{array}{llclll@{\qquad\qquad}l} \production{reference type} & \Breftype &::=& \hex{70} &\Rightarrow& \FUNCREF \\ &&|& - \hex{6F} &\Rightarrow& \EXTERNREF \\ + \hex{6F} &\Rightarrow& \EXTERNREF \\ &&|& + \hex{69} &\Rightarrow& \EXNREF \\ \end{array} diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index 7d6e5f4d..5d19010c 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -2683,123 +2683,184 @@ Control Instructions \end{array} -.. _exec-try-catch: +.. _exec-try_table: -:math:`\TRY~\blocktype~\instr_1^\ast~(\CATCH~x~\instr_2^\ast)^\ast~(\CATCHALL~\instr_3^\ast)^?~\END` -.................................................................................................... +:math:`\TRYTABLE~\blocktype~\catch^\ast~\instr^\ast~\END` +......................................................... 1. Assert: due to :ref:`validation `, :math:`\expand_F(\blocktype)` is defined. 2. Let :math:`[t_1^m] \to [t_2^n]` be the :ref:`function type ` :math:`\expand_F(\blocktype)`. -3. Let :math:`L` be the label whose arity is :math:`n` and whose continuation is the end of the |TRY| instruction. +3. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. -4. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. +4. Pop the values :math:`\val^m` from the stack. -5. Pop the values :math:`\val^m` from the stack. +5. Let :math:`L` be the label whose arity is :math:`n` and whose continuation is the end of the |TRYTABLE| instruction. -6. Let :math:`F` be the :ref:`current ` :ref:`frame `. +6. :ref:`Enter ` the block :math:`\val^m~\instr_1^\ast` with label :math:`L` and exception handler :math:`\HANDLER_n\{\catch^\ast\}`. -7. For each catch clause :math:`(\CATCH~x_i~\instr_{2i}^\ast)` do: +.. math:: + ~\\[-1ex] + \begin{array}{r} + F; \val^m~(\TRYTABLE~\X{bt}~\catch^\ast~\instr^\ast~\END + \quad \stepto \quad + F; \HANDLER_n\{\catch^\ast\}~(\LABEL_n\{\epsilon\}~\val^m~\instr^\ast~\END)~\END \\ \qquad\qquad + (\iff \expand_F(\X{bt}) = [t_1^m] \to [t_2^n] \land (F.\AMODULE.\MITAGS[x]=a_x)^\ast) + \end{array} + + +.. _exec-throw: + +:math:`\THROW~x` +................ + +1. Let :math:`F` be the :ref:`current ` :ref:`frame `. + +2. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITAGS[x]` exists. + +3. Let :math:`a` be the :ref:`tag address ` :math:`F.\AMODULE.\MITAGS[x]`. - a. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITAGS[x_i]` exists. +4. Assert: due to :ref:`validation `, :math:`S.\STAGS[a]` exists. - b. Let :math:`a_i` be the tag address :math:`F.\AMODULE.\MITAGS[x_i]`. +5. Let :math:`\X{ti}` be the :ref:`tag instance ` :math:`S.\STAGS[a]`. - c. Let :math:`H_i` be the handler :math:`(a_i~\instr_{2i}^\ast)`. +6. Let :math:`[t^n] \toF [{t'}^\ast]` be the :ref:`tag type ` :math:`\X{ti}.\TAGITYPE`. -8. If there is a catch all clause :math:`(\CATCHALL~\instr_3^\ast)`, then: +7. Assert: due to :ref:`validation `, there are at least :math:`n` values on the top of the stack. - a. Let :math:`H'^?` be the handler :math:`(\epsilon~\instr_3^\ast)`. +8. Pop the :math:`n` values :math:`\val^n` from the stack. -9. Else: +9. Let :math:`\X{exn}` be the :ref:`exception instance ` :math:`\{ \EITAG~a, \EIFIELDS~\val^n \}`. - a. Let :math:`H'^?` be the empty handler :math:`\epsilon`. +10. Let :math:`\X{ea}` be the length of :math:`S.\SEXNS`. -10. Let :math:`H^\ast` be the concatenation of :math:`H_i` and :math:`H'^?`. +11. Append :math:`\X{exn}` to :math:`S.\SEXNS`. -11. :ref:`Enter ` the block :math:`\val^m~\instr_1^\ast` with label :math:`L` and exception handler :math:`H^\ast`. +12. Push the value :math:`\REFEXNADDR~\X{ea}` to the stack. + +13. Execute the instruction |THROWREF|. .. math:: ~\\[-1ex] - \begin{array}{l} - F; \val^m~(\TRY~\X{bt}~\instr_1^\ast~(\CATCH~x~\instr_2^\ast)^\ast~(\CATCHALL~\instr_3^\ast)^?~\END - \quad \stepto \\ - \qquad F; \LABEL_n\{\epsilon\}~(\HANDLERadm_n\{(a_x~\instr_2^\ast)^\ast~(\epsilon~\instr_3^\ast)^?\}~\val^m~\instr_1^\ast~\END)~\END \\ - (\iff \expand_F(\X{bt}) = [t_1^m] \to [t_2^n] \land (F.\AMODULE.\MITAGS[x]=a_x)^\ast) + \begin{array}{lclr@{\qquad}l} + S; F; \val^n~(\THROW~x) &\stepto& S'; F; (\REFEXNADDR~|S.\SEXNS|)~\THROWREF & + (\begin{array}[t]{@{}r@{~}l@{}} + \iff & F.\AMODULE.\MITAGS[x] = a \\ + \land & S.\STAGS[a].\TAGITYPE = [t^n] \toF [] \\ + \land & \X{exn} = \{ \EITAG~a, \EIFIELDS~\val^n \} \\ + \land & S' = S \with \SEXNS = S.\SEXNS~\X{exn} ) \\ + \end{array} \\ \end{array} -.. _exec-try-delegate: +.. _exec-throw_ref: -:math:`\TRY~\blocktype~\instr^\ast~\DELEGATE~l` -............................................... +:math:`\THROWREF` +................. -1. Assert: due to :ref:`validation `, :math:`\expand_F(\blocktype)` is defined. +1. Let :math:`F` be the :ref:`current ` :ref:`frame `. -2. Let :math:`[t_1^m] \to [t_2^n]` be the :ref:`function type ` :math:`\expand_F(\blocktype)`. +2. Assert: due to :ref:`validation `, a :ref:`reference ` is on the top of the stack. -3. Let :math:`L` be the label whose arity is :math:`n` and whose continuation is the end of the |TRY| instruction. +3. Pop the reference :math:`\reff` from the stack. -4. Let :math:`H` be the :ref:`exception handler ` :math:`l`, targeting the :math:`l`-th surrounding block. +4. If :math:`\reff` is :math:`\REFNULL~\X{ht}`, then: -5. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. + a. Trap. -6. Pop the values :math:`\val^m` from the stack. +5. Assert: due to :ref:`validation `, :math:`\reff` is an :ref:`exception reference `. -7. :ref:`Enter ` the block :math:`\val^m~\instr^\ast` with label :math:`L` and exception handler `H`. +6. Let :math:`\REFEXNADDR~\X{ea}` be :math:`\reff`. -.. math:: - ~\\[-1ex] - \begin{array}{lcl} - F; \val^m~(\TRY~\X{bt}~\instr^\ast~\DELEGATE~l) &\stepto& - F; \LABEL_n\{\epsilon\}~(\HANDLERadm_n\{l\}~\val^m~\instr^\ast~\END)~\END \\ - && (\iff \expand_F(\X{bt}) = [t_1^m] \to [t_2^n]) - \end{array} +7. 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}]`. -.. _exec-throw: +9. Let :math:`a` be the :ref:`tag address ` :math:`\X{exn}.\EITAG`. -:math:`\THROW~x` -................ +10. While the stack is not empty and the top of the stack is not an :ref:`exception handler `, do: -1. Let :math:`F` be the :ref:`current ` :ref:`frame `. + a. Pop the top element from the stack. -2. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITAGS[x]` exists. +11. Assert: the stack is now either empty, or there is an exception handler on the top of the stack. -3. Let :math:`a` be the :ref:`tag address ` :math:`F.\AMODULE.\MITAGS[x]`. +12. If the stack is empty, then: -4. :ref:`Throw ` an exception with :ref:`tag address ` :math:`a`. + a. Return the exception :math:`(\REFEXNADDR~a)` as a :ref:`result `. -.. math:: - ~\\[-1ex] - \begin{array}{lclr@{\qquad}l} - \THROW~x &\stepto& \THROWadm~a & (\iff F.\AMODULE.\MITAGS[x] = a) \\ - \end{array} +13. 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. -.. _exec-rethrow: +15. If :math:`\catch^\ast` is empty, then: -:math:`\RETHROW~l` -.................. + a. Push the exception reference :math:`\REFEXNADDR~\X{ea}` back to the stack. -1. Assert: due to :ref:`validation `, the stack contains at least :math:`l+1` labels. + b. Execute the instruction |THROWREF| again. -2. Let :math:`L` be the :math:`l`-th label appearing on the stack, starting from the top and counting from zero. +16. Else: + + a. 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:`exception 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`. -3. Assert: due to :ref:`validation `, :math:`L` is a catch label, i.e., a label of the form :math:`(\LCATCH~[t^\ast])`, which is a label followed by a caught exception in an active catch clause. + c. Else if :math:`\catch_1` is of the form :math:`\CATCHREF~x~l` and the :ref:`exception address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: -4. Let :math:`\{a~\val^\ast\}` be the caught exception. + i. Push the values :math:`\X{exn}.\EIFIELDS` to the stack. -5. Push the values :math:`\val^\ast` onto the stack. + ii. Push the exception reference :math:`\REFEXNADDR~\X{ea}` to the stack. -6. :ref:`Throw ` an exception with :ref:`tag address ` :math:`a`. + iii. Execute the instruction :math:`\BR~l`. + + d. 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: + + i. Push the exception reference :math:`\REFEXNADDR~\X{ea}` to the stack. + + ii. Execute the instruction :math:`\BR~l`. + + f. Else: + + 1. Push the modified handler :math:`\HANDLER_n\{{\catch'}^\ast\}` back to the stack. + + 2. Push the exception reference :math:`\REFEXNADDR~\X{ea}` back to the stack. + + 3. Execute the instruction :math:`\THROWREF` again. .. math:: ~\\[-1ex] - \begin{array}{lclr@{\qquad}} - \CAUGHTadm_n\{a~\val^n\}~\XB^l[\RETHROW~l]~\END &\stepto& - \CAUGHTadm_n\{a~\val^n\}~\XB^l[\val^n~(\THROWadm~a)]~\END \\ + \begin{array}{rcl} + (\REFNULL~\X{ht})~\THROWREF &\stepto& + \TRAP \\ + \HANDLER_n\{\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END &\stepto& + (\REFEXNADDR~a)~\THROWREF \\ + S; F; \HANDLER_n\{(\CATCH~x~l)~\catch^\ast\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END &\stepto& + \X{exn}.\EIFIELDS~(\BR~l) \\ && + (\begin{array}[t]{@{}r@{~}l@{}} + \iff & \X{exn} = S.\SEXNS[a] \\ + \land & \X{exn}.\EITAG = F.\AMODULE.\MITAGS[x]) \\ + \end{array} \\ + S; F; \HANDLER_n\{(\CATCHREF~x~l)~\catch^\ast\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END &\stepto& + \X{exn}.\EIFIELDS~(\REFEXNADDR~a)~(\BR~l) \\ && + (\begin{array}[t]{@{}r@{~}l@{}} + \iff & \X{exn} = S.\SEXNS[a] \\ + \land & \X{exn}.\EITAG = F.\AMODULE.\MITAGS[x]) \\ + \end{array} \\ + \HANDLER_n\{(\CATCHALL~l)~\catch^\ast\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END &\stepto& + (\BR~l) \\ + \HANDLER_n\{(\CATCHALLREF~l)~\catch^\ast\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END &\stepto& + (\REFEXNADDR~a)~(\BR~l) \\ + \HANDLER_n\{\catch_1~\catch^\ast\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END &\stepto& + \HANDLER_n\{\catch^\ast\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END \\ && + (\otherwise) \\ \end{array} @@ -2820,9 +2881,9 @@ Control Instructions 6. Repeat :math:`l+1` times: - a. While the top of the stack is a value, a |handler|, or a |CAUGHTadm| instruction, do: + a. While the top of the stack is a value or a :ref:`handler `, do: - i. Pop the value from the stack. + i. Pop the value or handler from the stack. b. Assert: due to :ref:`validation `, the top of the stack now is a label. @@ -3074,13 +3135,12 @@ When the end of a block is reached without a jump, exception, or trap aborting i .. index:: exception handling, throw context, tag, exception tag pair: handling; exception -.. _exec-catchadm: -.. _exec-delegateadm: +.. _exec-handler: Exception Handling ~~~~~~~~~~~~~~~~~~ -The following auxiliary rules define the semantics of entering and exiting :ref:`exception handlers ` through :ref:`try ` instructions, and handling thrown exceptions. +The following auxiliary rules define the semantics of entering and exiting |TRYTABLE| blocks. .. _exec-handler-enter: @@ -3097,163 +3157,36 @@ Entering :math:`\instr^\ast` with label :math:`L` and exception handler :math:`H .. note:: No formal reduction rule is needed for entering an exception :ref:`handler ` because it is an :ref:`administrative instruction ` - that the :ref:`try ` instruction reduces to directly. + that the |TRYTABLE| instruction reduces to directly. .. _exec-handler-exit: Exiting an exception handler ............................ -When the end of a :ref:`try ` instruction is reached without a jump, exception, or trap, then the following steps are performed. +When the end of a |TRYTABLE| block is reached without a jump, exception, or trap, then the following steps are performed. 1. Let :math:`m` be the number of values on the top of the stack. 2. Pop the values :math:`\val^m` from the stack. -3. Assert: due to :ref:`validation `, the handler :math:`H` is now on the top of the stack. +3. Assert: due to :ref:`validation `, a handler and a label are now on the top of the stack. + +4. Pop the label from the stack. -4. Pop the handler from the stack. +5. Pop the handler :math:`H` from the stack. -5. Push :math:`\val^m` back to the stack. +6. Push :math:`\val^m` back to the stack. -6. Jump to the position after the |END| of the administrative instruction associated with the handler :math:`H`. +7. Jump to the position after the |END| of the administrative instruction associated with the handler :math:`H`. .. math:: ~\\[-1ex] \begin{array}{lcl@{\qquad}l} - \HANDLERadm_m\{\handler\}~\val^m~\END &\stepto& \val^m \\ + \HANDLER_m\{\catch^\ast\}~\val^m~\END &\stepto& \val^m \\ \end{array} -.. _exec-throwadm: - -Throwing an exception with :ref:`tag address ` :math:`a` -........................................................................ - -When a throw occurs, then values, labels, active catch clauses, -and call frames are popped if necessary, until an appropriate exception handler is found -on the top of the stack. - - 1. Assert: due to :ref:`validation `, :math:`S.\STAGS[a]` exists. - - 2. Let :math:`[t^n] \to []` be the :ref:`tag type ` :math:`S.\STAGS[a].\TAGITYPE`. - - 3. Assert: due to :ref:`validation `, there are :math:`n` values on the top of the stack. - - 4. Pop the :math:`n` values :math:`\val^n` from the stack. - - 5. 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. - - 6. Assert: the stack is now either empty, or there is an exception handler on the top of the stack. - - 7. If the stack is empty, then: - - a. Return the uncaught exception :math:`\val^n~(\THROWadm~a)` as a :ref:`result `. - -8. Else assert: there is an :ref:`exception handler ` :math:`H` on the top of the stack. - -9. Pop the exception handler :math:`H` from the stack. - -10. If :math:`H` is list of handlers, then: - - a. While :math:`H` is not empty, do: - - i. Let :math:`(a_1^?~\instr^\ast)` be the first handler in :math:`H`. - - ii. If :math:`a_1^? = \epsilon`, then: - - * :ref:`Enter ` the block :math:`\instr^\ast` with caught exception :math:`a~\val^n`. - - iii. Else if :math:`a_1^? = a`, then: - - * :ref:`Enter ` the block :math:`\val^n~\instr^\ast` with caught exception :math:`a~\val^n`. - - iv. Else, pop the first handler from :math:`H`. - - b. Else, the exception was not caught by :math:`H`: - - i. Put the values :math:`\val^n` back onto the stack. - - ii. :ref:`Throw ` an exception with tag address :math:`a`. - -11. Else :math:`H` is a label index :math:`l`. - - a. Assert: due to :ref:`validation `, the stack contains at least :math:`l` labels. - - b. Repeat :math:`l` times: - - i. While the top of the stack is not a label, do: - - * Pop the top element from the stack. - - c. Assert: due to :ref:`validation `, the top of the stack now is a label. - - d. Pop the label from the stack. - - e. Push the values :math:`\val^n` onto the stack. - - f. :ref:`Throw ` an exception with tag address :math:`a`. - -.. math:: - \begin{array}{rcl} - \HANDLERadm_n\{\}~\XT[(\THROWadm~a)]~\END &\stepto& - \XT[(\THROWadm~a)] \\ - \HANDLERadm_n\{(a_1^?~\instr^\ast)~(a'^?~\instr'^\ast)^\ast\}~\XT[(\THROWadm~a)]~\END &\stepto& - \HANDLERadm_n\{(a'^?~\instr'^\ast)^\ast~\XT[(\THROWadm~a)]~\END \\ - && (\iff a_1^? \neq \epsilon \land a_1^? \neq a) \\ - S;~\HANDLERadm_n\{(a_1^?~\instr^\ast)~(a'^?~\instr'^\ast)^\ast\}~\XT[\val^n~(\THROWadm~a)]~\END &\stepto& - S;~\CAUGHTadm_n\{a~\val^n\}~(\val^n)^?~\instr^\ast~\END \\ - && (\iff~(a_1^? = \epsilon \lor a_1^? = a)~\land\\ - && \ S.\STAGS[a].\TAGITYPE = [t^n]\to[]) \\ - \LABEL_n\{\}~\XB^l[\HANDLERadm_n\{l\}~\XT[(\THROWadm~a)]~\END]~\END &\stepto& - \XT[(\THROWadm~a)] \\ - \end{array} - -.. note:: - The rules are formulated in this way to allow looking up the exception values in the throw context, - only when a thrown exception is caught. - - -.. _exec-caughtadm-enter: - -Entering :math:`\instr^\ast` with caught exception :math:`\{\exn\}` -................................................................... - -1. Push the caught exception |exn| onto the stack. - -2. Jump to the start of the instruction sequence :math:`\instr^\ast`. - - -.. _exec-caughtadm-exit: - -Exiting a block with a caught exception -....................................... - -When the |END| of a block with a caught exception is reached without a jump, thrown exception, or trap, then the following steps are performed. - -1. Let :math:`\val^m` be the values on the top of the stack. - -2. Pop the values :math:`\val^m` from the stack. - -3. Assert: due to :ref:`validation `, a caught exception is now on the top of the stack. - -4. Pop the caught exception from the stack. - -5. Push :math:`\val^m` back to the stack. - -6. Jump to the position after the |END| of the administrative instruction associated with the caught exception. - -.. math:: - \begin{array}{rcl} - \CAUGHTadm_n\{\exn\}~\val^m~\END &\stepto& \val^m - \end{array} - -.. note:: - A caught exception can only be rethrown from the scope of the administrative instruction associated with it, i.e., from the scope of the |CATCH| or |CATCHALL| block of a :ref:`try-catch ` instruction that caught it. Upon exit from that block, the caught exception is discarded. - - .. index:: ! call, function, function instance, label, frame Function Calls diff --git a/document/core/exec/modules.rst b/document/core/exec/modules.rst index f81a4231..a43f55e9 100644 --- a/document/core/exec/modules.rst +++ b/document/core/exec/modules.rst @@ -157,8 +157,23 @@ The following auxiliary typing rules specify this typing relation relative to a } -:ref:`External References ` :math:`\REFEXTERNADDR~a` -....................................................................... +:ref:`Exception References ` :math:`\REFEXNADDR~a` +.............................................................. + +* The store entry :math:`S.\SEXNS[a]` must exist. + +* Then the value is valid with :ref:`reference type ` :math:`\EXNREF`. + +.. math:: + \frac{ + S.\SEXNS[a] = \exninst + }{ + S \vdashval \REFEXNADDR : \EXNREF + } + + +:ref:`External References ` :math:`\REFEXTERNADDR~a` +............................................................................ * The value is valid with :ref:`reference type ` :math:`\EXTERNREF`. diff --git a/document/core/exec/runtime.rst b/document/core/exec/runtime.rst index 9acda299..021a9dcd 100644 --- a/document/core/exec/runtime.rst +++ b/document/core/exec/runtime.rst @@ -12,7 +12,9 @@ Runtime Structure .. _syntax-num: .. _syntax-vecc: .. _syntax-ref: -.. _syntax-ref.extern: +.. _syntax-ref.func-addr: +.. _syntax-ref.exn-addr: +.. _syntax-ref.extern-addr: .. _syntax-val: Values @@ -26,7 +28,8 @@ It is convenient to reuse the same notation as for the |CONST| :ref:`instruction References other than null are represented with additional :ref:`administrative instructions `. They either are *function references*, pointing to a specific :ref:`function address `, -or *external references* pointing to an uninterpreted form of :ref:`extern address ` that can be defined by the :ref:`embedder ` to represent its own objects. +*exception references*, pointing to a specific :ref:`exception address `, +or *external references* pointing to an uninterpreted form of :ref:`external address ` that can be defined by the :ref:`embedder ` to represent its own objects. .. math:: \begin{array}{llcl} @@ -40,6 +43,7 @@ or *external references* pointing to an uninterpreted form of :ref:`extern addre \production{reference} & \reff &::=& \REFNULL~t \\&&|& \REFFUNCADDR~\funcaddr \\&&|& + \REFEXNADDR~\exnaddr \\&&|& \REFEXTERNADDR~\externaddr \\ \production{value} & \val &::=& \num ~|~ \vecc ~|~ \reff \\ @@ -67,7 +71,7 @@ Convention * The meta variable :math:`r` ranges over reference values where clear from context. -.. index:: ! result, value, trap, exception +.. index:: ! result, value, trap, exception, exception address pair: abstract syntax; result .. _syntax-result: @@ -75,16 +79,17 @@ Results ~~~~~~~ A *result* is the outcome of a computation. -It is either a sequence of :ref:`values `, a :ref:`trap `, or an :ref:`exception `. +It is either a sequence of :ref:`values `, a :ref:`trap `, or a thrown :ref:`exception `. .. math:: \begin{array}{llcl} \production{result} & \result &::=& \val^\ast \\&&|& \TRAP \\&&|& - \XT[(\THROWadm~\tagaddr)] + \XT[(\REFEXNADDR~\exnaddr)~\THROWREF] \end{array} + .. index:: ! store, function instance, table instance, memory instance, tag instance, global instance, module, allocation pair: abstract syntax; store .. _syntax-store: @@ -94,7 +99,7 @@ Store ~~~~~ The *store* represents all global state that can be manipulated by WebAssembly programs. -It consists of the runtime representation of all *instances* of :ref:`functions `, :ref:`tables `, :ref:`memories `, :ref:`tags `, and :ref:`globals `, :ref:`element segments `, and :ref:`data segments ` that have been :ref:`allocated ` during the life time of the abstract machine. [#gc]_ +It consists of the runtime representation of all *instances* of :ref:`functions `, :ref:`tables `, :ref:`memories `, :ref:`tags `, and :ref:`globals `, :ref:`element segments `, :ref:`data segments `, and :ref:`exceptions ` that have been :ref:`allocated ` during the life time of the abstract machine. [#gc]_ It is an invariant of the semantics that no element or data instance is :ref:`addressed ` from anywhere else but the owning module instances. @@ -110,12 +115,13 @@ Syntactically, the store is defined as a :ref:`record ` listing \STAGS & \taginst^\ast, \\ \SGLOBALS & \globalinst^\ast, \\ \SELEMS & \eleminst^\ast, \\ - \SDATAS & \datainst^\ast ~\} \\ + \SDATAS & \datainst^\ast, \\ + \SEXNS & \exninst^\ast ~\} \\ \end{array} \end{array} .. [#gc] - In practice, implementations may apply techniques like garbage collection to remove objects from the store that are no longer referenced. + In practice, implementations may apply techniques like garbage collection or reference counting to remove objects from the store that are no longer referenced. However, such techniques are not semantically observable, and hence outside the scope of this specification. @@ -134,6 +140,7 @@ Convention pair: abstract syntax; global address pair: abstract syntax; element address pair: abstract syntax; data address + pair: abstract syntax; exception address pair: abstract syntax; host address pair: function; address pair: table; address @@ -142,6 +149,7 @@ Convention pair: global; address pair: element; address pair: data; address + pair: exception; address pair: host; address .. _syntax-funcaddr: .. _syntax-tableaddr: @@ -150,13 +158,14 @@ Convention .. _syntax-globaladdr: .. _syntax-elemaddr: .. _syntax-dataaddr: +.. _syntax-exnaddr: .. _syntax-externaddr: .. _syntax-addr: Addresses ~~~~~~~~~ -:ref:`Function instances `, :ref:`table instances `, :ref:`memory instances `, :ref:`tag instances `, :ref:`global instances `, :ref:`element instances `, and :ref:`data instances ` in the :ref:`store ` are referenced with abstract *addresses*. +:ref:`Function instances `, :ref:`table instances `, :ref:`memory instances `, :ref:`tag instances `, :ref:`global instances `, :ref:`element instances `, :ref:`data instances `, and :ref:`exception instances ` in the :ref:`store ` are referenced with abstract *addresses*. These are simply indices into the respective store component. In addition, an :ref:`embedder ` may supply an uninterpreted set of *host addresses*. @@ -178,6 +187,8 @@ In addition, an :ref:`embedder ` may supply an uninterpreted set of *h \addr \\ \production{data address} & \dataaddr &::=& \addr \\ + \production{exception address} & \exnaddr &::=& + \addr \\ \production{extern address} & \externaddr &::=& \addr \\ \end{array} @@ -444,6 +455,24 @@ It filters out entries of a specific kind in an order-preserving fashion: * :math:`\evglobals(\externval^\ast) = [\globaladdr ~|~ (\EVGLOBAL~\globaladdr) \in \externval^\ast]` +.. index:: ! exception instance, tag, tag address, + pair: abstract syntax; exception instance + pair: exception; instance +.. _syntax-exninst: + +Exception Instances +~~~~~~~~~~~~~~~~~~~ + +An *exception instance* is the runtime representation of an _exception_ produced by a |THROW| instruction. +It holds the :ref:`address ` of the respective :ref:`tag ` and the argument :ref:`values `. + +.. math:: + \begin{array}{llcl} + \production{exception instance} & \exninst &::=& + \{ \EITAG~\tagaddr, \EIFIELDS~\vec(\val) \} \\ + \end{array} + + .. index:: ! stack, ! frame, ! label, ! handler, instruction, store, activation, function, call, local, module instance, exception handler, exception pair: abstract syntax; frame @@ -451,17 +480,17 @@ It filters out entries of a specific kind in an order-preserving fashion: pair: abstract syntax; handler .. _syntax-frame: .. _syntax-label: +.. _syntax-handler: .. _frame: .. _label: .. _handler: -.. _exn: .. _stack: Stack ~~~~~ Besides the :ref:`store `, most :ref:`instructions ` interact with an implicit *stack*. -The stack contains five kinds of entries: +The stack contains the following kinds of entries: * *Values*: the *operands* of instructions. @@ -471,8 +500,6 @@ The stack contains five kinds of entries: * *Handlers*: active exception handlers. -* *Exceptions*: caught exceptions. - These entries can occur on the stack in any order during the execution of a program. Stack entries are described by abstract syntax as follows. @@ -528,38 +555,18 @@ and a reference to the function's own :ref:`module instance ` The values of the locals are mutated by respective :ref:`variable instructions `. -.. _syntax-handler: -.. _syntax-exn: - -Exception handlers and exceptions -................................. - -Exception handlers are installed by |TRY| instructions and are either a list of handlers or a label index. +Exception Handlers +.................. -A list of handlers is a mapping from :ref:`tag addresses ` -to their associated branch *targets*. A single handler is expressed syntactically as a possibly empty sequence of -:ref:`instructions ` possibly following a :ref:`tag address `. -If there is no :ref:`tag address `, the instructions of that handler correspond to a |CATCHALL| clause. - -An exception may be temporarily pushed onto the stack when it is :ref:`thrown and caught ` by a handler. - -A handler can also consist of a single |labelidx|, which denotes an outer block to which every caught exception will be delegated, by implicitly rethrowing inside that block. -This handler does not catch exceptions, but only rethrows them. +Exception handlers are installed by |TRYTABLE| instructions and record the corresponding list of :ref:`catch clauses `: .. math:: \begin{array}{llllll} - \production{handler} & \handler &::=& (\tagaddr^?~\instr^\ast)^\ast &|& \labelidx\\ - \production{exception} & \exn &::=& \tagaddr~\val^\ast && + \production{handler} & \handler &::=& + \HANDLER_n\{\catch^\ast\} \end{array} -Intuitively, for each individual handler :math:`(\tagaddr^?~\instr^\ast)`, the instruction block :math:`\instr^\ast` is the *continuation* to execute -when the handler catches a thrown exception with tag |tagaddr|, or for any exception, when that handler specifies no tag address. -If the list of handlers is empty, or if the tag address of the thrown exception is not in any of the handlers in the list, and there is no |CATCHALL| clause, then the exception will be rethrown. - -When a thrown exception is caught by a handler, the caught exception is pushed onto the stack and the block of that handler's target is :ref:`entered `. -When exiting a block with a caught exception, the exception is discarded. - -A handler consisting of a |labelidx| :math:`l` can be thought of as a branch to that label that happens in case an exception occurs, immediately followed by a rethrow of the exception at the target site. +The handlers on the stack are searched when an exception is :ref:`thrown `. .. _exec-expand: @@ -585,11 +592,7 @@ Conventions .. index:: ! administrative instructions, function, function instance, function address, label, frame, instruction, trap, call, memory, memory instance, table, table instance, element, data, segment, tag, tag instance, tag address, exception, reftype, handler, caught, caught exception pair:: abstract syntax; administrative instruction .. _syntax-trap: -.. _syntax-reffuncaddr: .. _syntax-invoke: -.. _syntax-throwadm: -.. _syntax-handleradm: -.. _syntax-caughtadm: .. _syntax-instr-admin: Administrative Instructions @@ -607,27 +610,24 @@ In order to express the reduction of :ref:`traps `, :ref:`calls `. Similarly, |REFEXTERNADDR| represents :ref:`external references `. +The |REFFUNCADDR| instruction represents :ref:`function reference values `. +Similarly, |REFEXTERNADDR| represents :ref:`external references `. +The |REFEXNADDR| instruction represents an :ref:`exception reference `. The |INVOKE| instruction represents the imminent invocation of a :ref:`function instance `, identified by its :ref:`address `. It unifies the handling of different forms of calls. -The |THROWadm| instruction represents the imminent throw of an exception based on a :ref:`tag instance `, identified by its :ref:`address `. -The values it will consume depend on its :ref:`tag type `. -It unifies the different forms of throwing exceptions. - -The |LABEL|, |FRAME|, |HANDLERadm|, and |CAUGHTadm| instructions model :ref:`labels `, :ref:`frames `, active :ref:`exception handlers `, and :ref:`caught exceptions `, respectively, :ref:`"on the stack" `. +The |LABEL|, |FRAME|, and |HANDLER| instructions model :ref:`labels `, :ref:`frames `, and active :ref:`exception handlers `, respectively, :ref:`"on the stack" `. Moreover, the administrative syntax maintains the nesting structure of the original :ref:`structured control instruction ` or :ref:`function body ` and their :ref:`instruction sequences ` with an |END| marker. That way, the end of the inner instruction sequence is known when part of an outer sequence. @@ -670,24 +670,16 @@ In order to specify the reduction of :ref:`branches `, the .. math:: \begin{array}{llll} - \production{block contexts} & \XB^0 &::=& - \val^\ast~[\_]~\instr^\ast \\ - \production{block contexts} & \XB^{k+1} &::=& - \val^\ast~\LABEL_n\{\instr^\ast\}~\XB^k~\END~\instr^\ast \\ + \production{block contexts} & \XB^k &::=& + \val~\XB^k ~|~ \XB^k~\instr ~|~ \HANDLER_n\{\catch^\ast\}~\XB^k~\END ~|~ \XC^k \\ + \production{label contexts} & \XC^0 &::=& + [\_] \\ + \production{label contexts} & \XC^{k+1} &::=& + \LABEL_n\{\instr^\ast\}~\XB^k~\END \\ \end{array} This definition allows to index active labels surrounding a :ref:`branch ` or :ref:`return ` instruction. -In order to be able to break jumping over exception handlers and caught exceptions, these new structured administrative control instructions are allowed to appear after labels in block contexts, by extending block context as follows. - -.. math:: - \begin{array}{llll} - \production{control contexts} & \XC^{k} &::=& \HANDLERadm_n\{\handler\}~\XB^k~\END \\ - & & | & \CAUGHTadm_n~\{\exn\}~\XB^k~\END \\ - \production{block contexts} & \XB^0 &::=& \dots ~|~ \val^\ast~\XC^0~\instr^\ast\\ - \production{block contexts} & \XB^{k+1} &::=& \dots ~|~ \val^\ast~\XC^{k+1}~\instr^\ast \\ - \end{array} - .. note:: For example, the :ref:`reduction ` of a simple branch can be defined as follows: @@ -696,18 +688,17 @@ In order to be able to break jumping over exception handlers and caught exceptio Here, the hole :math:`[\_]` of the context is instantiated with a branch instruction. When a branch occurs, - this rule replaces the targeted label and associated instruction sequence with the label's continuation. + this rule replaces the target label and associated instruction sequence with the label's continuation. The selected label is identified through the :ref:`label index ` :math:`l`, which corresponds to the number of surrounding |LABEL| instructions that must be hopped over -- which is exactly the count encoded in the index of a block context. -.. index:: ! throw context, tag, throw address, catch block, handler, exception +.. index:: ! throw context, tag, throw address, catch clause, handler, exception .. _syntax-ctxt-throw: Throw Contexts .............. -In order to specify the reduction of |TRY| blocks -with the help of the administrative instructions |THROWadm|, |HANDLERadm|, and |CAUGHTadm|, +In order to specify the reduction of |TRYTABLE| blocks, the following syntax of *throw contexts* is defined, as well as associated structural rules: .. math:: @@ -716,48 +707,13 @@ the following syntax of *throw contexts* is defined, as well as associated struc [\_] \\ &&|& \val^\ast~\XT~\instr^\ast \\ &&|& \LABEL_n\{\instr^\ast\}~\XT~\END \\ &&|& - \CAUGHTadm_n\{\exn\}~\XT~\END \\ &&|& \FRAME_n\{F\}~\XT~\END \\ \end{array} -Throw contexts allow matching the program context around a throw instruction up to the innermost enclosing |HANDLERadm|, thereby selecting the exception |handler| responsible for an exception, if one exists. -If no exception :ref:`handler that catches the exception ` is found, the computation :ref:`results ` in an uncaught exception result value. +Throw contexts allow matching the program context around a throw instruction up to the innermost enclosing :ref:`exception handler `, if one exists. .. note:: - Contrary to block contexts, throw contexts don't skip over handlers. - - |CAUGHTadm| blocks do not represent active handlers. Instead, they delimit the continuation of a handler that has already been selected. Their sole purpose is to record the exception that has been caught, such that |RETHROW| can access it inside such a block. - -.. note:: - For example, catching a simple :ref:`throw ` in a :ref:`try block ` would be as follows. - - Assume that :math:`\expand_F(bt) = [\I32~\F32~\I64] \to [\F32~\I64]`, - and that the tag address `a` of :math:`x` has tag type :math:`[\F32~\I64] \to []`. - Let :math:`\val_{i32}`, :math:`\val_{f32}`, and :math:`\val_{i64}` be values of type |I32|, |F32|, and |I64| respectively. - - .. math:: - \begin{array}{ll} - & \hspace{-5ex} F;~\val_{i32}~\val_{f32}~\val_{i64}~(\TRY~\X{bt}~(\THROW~x)~\CATCH~x~\END) \\ - \stepto & F;~\LABEL_2\{\} (\HANDLERadm_2\{(a~\epsilon)\}~\val_{i32}~\val_{f32}~\val_{i64}~(\THROW~x)~\END)~\END \\ - \end{array} - - :ref:`Handling the thrown exception ` with tag address :math:`a` in the throw context - :math:`T=[\val_{i32}\_]`, with the exception handler :math:`H=(a~\epsilon)` gives: - - .. math:: - \begin{array}{lll} - \stepto & F;~\LABEL_2\{\}~(\CAUGHTadm_2\{a~\val_{f32}~\val_{i64}\}~\val_{f32}~\val_{i64}~\END)~\END & \hspace{9ex}\ \\ - \stepto & F;~\LABEL_2\{\}~\val_{f32}~\val_{i64}~\END & \hspace{9ex}\ \\ - \stepto & \val_{f32}~\val_{i64} & \\ - \end{array} - - - When a throw of the form :math:`\val^m (\THROWadm~a)` occurs, search for an enclosing exception handler is performed, - which means any throw context (that is any other values, labels, frames, and |CAUGHTadm| instructions) surrounding the throw :math:`\val^m (\THROWadm~a)` is popped, - until a :ref:`handler ` for the exception tag :math:`a` is found. - Then the :ref:`caught exception ` containing the tag address :math:`a` and the values :math:`\val^m`, is pushed onto the stack. - - In this particular case, the exception is caught by the exception handler :math:`H` and its values are returned. + Contrary to block contexts, throw contexts do not skip over handlers. .. index:: ! configuration, ! thread, store, frame, instruction, module instruction diff --git a/document/core/syntax/instructions.rst b/document/core/syntax/instructions.rst index e78dd998..fa835ebf 100644 --- a/document/core/syntax/instructions.rst +++ b/document/core/syntax/instructions.rst @@ -634,6 +634,7 @@ The |DATADROP| instruction prevents further use of a passive data segment. This .. _syntax-call_indirect: .. _syntax-instr-seq: .. _syntax-instr-control: +.. _syntax-catch: Control Instructions ~~~~~~~~~~~~~~~~~~~~ @@ -651,32 +652,35 @@ Instructions in this group affect the flow of control. \BLOCK~\blocktype~\instr^\ast~\END \\&&|& \LOOP~\blocktype~\instr^\ast~\END \\&&|& \IF~\blocktype~\instr^\ast~\ELSE~\instr^\ast~\END \\&&|& - \TRY~\blocktype~\instr^\ast~(\CATCH~\tagidx~\instr^\ast)^\ast~(\CATCHALL~\instr^\ast)^?~\END \\ &&|& - \TRY~\blocktype~\instr^\ast~\DELEGATE~\labelidx \\ &&|& + \TRYTABLE~\blocktype~\catch^\ast~\instr^\ast~\END \\ &&|& \THROW~\tagidx \\&&|& - \RETHROW~\labelidx \\ &&|& + \THROWREF \\ &&|& \BR~\labelidx \\&&|& \BRIF~\labelidx \\&&|& \BRTABLE~\vec(\labelidx)~\labelidx \\&&|& \RETURN \\&&|& \CALL~\funcidx \\&&|& \CALLINDIRECT~\tableidx~\typeidx \\ + \production{catch clause} & \catch &::=& + \CATCH~\tagidx~\labelidx \\ &&|& + \CATCHREF~\tagidx~\labelidx \\ &&|& + \CATCHALL~\labelidx \\ &&|& + \CATCHALLREF~\labelidx \\ \end{array} The |NOP| instruction does nothing. The |UNREACHABLE| instruction causes an unconditional :ref:`trap `. -The |BLOCK|, |LOOP|, |IF|, and |TRY| instructions are *structured* instructions. +The |BLOCK|, |LOOP|, |IF|, and |TRYTABLE| instructions are *structured* instructions. They bracket nested sequences of instructions, called *blocks*, -separated by either |ELSE|, |CATCH|, or |CATCHALL| pseudo-instructions, -and terminated with either an |END| or a |DELEGATE| pseudo-instruction. +separated by the |ELSE| pseudo-instruction, +and terminated with an |END| pseudo-instruction. As the grammar prescribes, they must be well-nested. -The instructions |TRY|, |THROW|, and |RETHROW|, are concerned with handling exceptions. -The |TRY| instruction installs an exception handler, and may either handle exceptions in the case of |CATCH| and |CATCHALL|, -or rethrow them in an outer block in the case of |DELEGATE|. -The |THROW| and |RETHROW| instructions alter control flow by searching for a matching handler in one of the enclosing |TRY| blocks, if any. +The instructions |TRYTABLE|, |THROW|, and |THROWREF| are concerned with *exceptions*. +The |TRYTABLE| instruction installs an exception handler that handles exceptions as specified by its catch clauses.. +The |THROW| and |THROWREF| instructions raise and reraise an exception, repsectively, and transfers control to the innermost enclosing exception handler that has a matching catch clause. A structured instruction can consume *input* and produce *output* on the operand stack according to its annotated *block type*. It is given either as a :ref:`type index ` that refers to a suitable :ref:`function type `, or as an optional :ref:`value type ` inline, which is a shorthand for the function type :math:`[] \to [\valtype^?]`. @@ -694,9 +698,6 @@ In case of |BLOCK| or |IF| it is a *forward jump*, resuming execution after the matching |END|. In case of |LOOP| it is a *backward jump* to the beginning of the loop. -When |TRY|--|DELEGATE| handles an exception, it also behaves similar to a forward jump, -effectively rethrowing the caught exception right before the matching |END|. - .. note:: This enforces *structured control flow*. Intuitively, a branch targeting a |BLOCK| or |IF| behaves like a :math:`\K{break}` statement in most C-like languages, diff --git a/document/core/syntax/types.rst b/document/core/syntax/types.rst index b1c164cc..9e0f8e40 100644 --- a/document/core/syntax/types.rst +++ b/document/core/syntax/types.rst @@ -85,11 +85,13 @@ Reference Types .. math:: \begin{array}{llll} \production{reference type} & \reftype &::=& - \FUNCREF ~|~ \EXTERNREF \\ + \FUNCREF ~|~ \EXNREF ~|~ \EXTERNREF \\ \end{array} The type |FUNCREF| denotes the infinite union of all references to :ref:`functions `, regardless of their :ref:`function types `. +The type |EXNREF| denotes the infinite union of all references to :ref:`exceptions `, regardless of their associated :ref:`tag types `. + The type |EXTERNREF| denotes the infinite union of all references to objects owned by the :ref:`embedder ` and that can be passed into WebAssembly under this type. Reference types are *opaque*, meaning that neither their size nor their bit pattern can be observed. diff --git a/document/core/text/instructions.rst b/document/core/text/instructions.rst index 07882dc2..d4f47253 100644 --- a/document/core/text/instructions.rst +++ b/document/core/text/instructions.rst @@ -62,11 +62,11 @@ Control Instructions .. _text-loop: .. _text-if: .. _text-instr-block: -.. _text-try: +.. _text-try_table: +.. _text-catch: :ref:`Structured control instructions ` can bind an optional symbolic :ref:`label identifier `. -The same label identifier may optionally be repeated after the corresponding :math:`\T{end}`, :math:`\T{else}`, :math:`\T{catch}`, :math:`\T{catch\_all}`, and :math:`\T{delegate}` -pseudo instructions, to indicate the matching delimiters. +The same label identifier may optionally be repeated after the corresponding :math:`\T{end}` or :math:`\T{else}` keywords, to indicate the matching delimiters. Their :ref:`block type ` is given as a :ref:`type use `, analogous to the type of :ref:`functions `. However, the special case of a type use that is syntactically empty or consists of only a single :ref:`result ` is not regarded as an :ref:`abbreviation ` for an inline :ref:`function type `, but is parsed directly into an optional :ref:`value type `. @@ -91,15 +91,22 @@ However, the special case of a type use that is syntactically empty or consists \text{else}~~\Tid_1^?~~(\X{in}_2{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid_2^? \\ &&&\qquad \Rightarrow\quad \IF~\X{bt}~\X{in}_1^\ast~\ELSE~\X{in}_2^\ast~\END \qquad (\iff \Tid_1^? = \epsilon \vee \Tid_1^? = \Tlabel, \Tid_2^? = \epsilon \vee \Tid_2^? = \Tlabel) \\ &&|& - \text{try}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype~~(\X{in}_1{:}\Tinstr_{I'})^\ast~~ - (\text{catch}~~\Tid_1^?~~x{:}\Ttagidx_I~~(\X{in}_2{:}\Tinstr_{I'})^\ast)^\ast~~ - \\ &&&\qquad\qquad (\text{catch\_all}~~\Tid_1^?~~(\X{in}_3{:}\Tinstr_{I'})^\ast)^?~~\text{end}~~\Tid_2^? - \\ &&&\qquad \Rightarrow\quad \TRY~\X{bt}~\X{in}_1^\ast~(\CATCH~x~\X{in}_2^\ast)^\ast~(\CATCHALL~\X{in}_3^\ast)^?~\END - \\ &&&\qquad\qquad (\iff \Tid_1^? = \epsilon \vee \Tid_1^? = \Tlabel, \Tid_2^? = \epsilon \vee \Tid_2^? = \Tlabel) \\ &&|& - \text{try}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype~~(\X{in}_1{:}\Tinstr_{I'})^\ast - ~~\text{delegate}~~l{:}\Tlabelidx_I~~\X{l}{:}\Tlabelidx_I - \\ &&&\qquad \Rightarrow\quad \TRY~\X{bt}~\X{in}_1^\ast~\DELEGATE~l - \qquad\quad~~ (\iff \Tid^? = \epsilon \vee \Tid^? = \Tlabel) \\ + \text{try\_table}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype~~(c{:}\Tcatch_I)^\ast~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid^? + \\ &&&\qquad \Rightarrow\quad \TRYTABLE~\X{bt}~c^\ast~\X{in}^\ast~~\END + \qquad\qquad (\iff \Tid^? = \epsilon \vee \Tid^? = \Tlabel) \\ + \production{catch clause} & \Tcatch_I & + \begin{array}[t]{@{}c@{}} ::= \\ | \\ | \\ | \\ \end{array} + & + \begin{array}[t]{@{}lcll@{}} + \text{(}~\text{catch}~~x{:}\Ttagidx_I~~l{:}\Tlabelidx_I~\text{)} + &\Rightarrow& \CATCH~x~l \\ + \text{(}~\text{catch\_ref}~~x{:}\Ttagidx_I~~l{:}\Tlabelidx_I~\text{)} + &\Rightarrow& \CATCHREF~x~l \\ + \text{(}~\text{catch\_all}~~l{:}\Tlabelidx_I~\text{)} + &\Rightarrow& \CATCHALL~l \\ + \text{(}~\text{catch\_all\_ref}~~l{:}\Tlabelidx_I~\text{)} + &\Rightarrow& \CATCHALLREF~l \\ + \end{array} \\ \end{array} .. note:: @@ -109,7 +116,7 @@ However, the special case of a type use that is syntactically empty or consists .. _text-nop: .. _text-unreachable: .. _text-throw: -.. _text-rethrow: +.. _text-throw_ref: .. _text-br: .. _text-br_if: .. _text-br_table: @@ -125,7 +132,7 @@ All other control instruction are represented verbatim. \text{unreachable} &\Rightarrow& \UNREACHABLE \\ &&|& \text{nop} &\Rightarrow& \NOP \\ &&|& \text{throw}~~x{:}\Ttagidx_I &\Rightarrow& \THROW~x \\ &&|& - \text{rethrow}~~l{:}\Tlabelidx_I &\Rightarrow& \RETHROW~l \\ &&|& + \text{throw\_ref} &\Rightarrow& \THROWREF \\ &&|& \text{br}~~l{:}\Tlabelidx_I &\Rightarrow& \BR~l \\ &&|& \text{br\_if}~~l{:}\Tlabelidx_I &\Rightarrow& \BRIF~l \\ &&|& \text{br\_table}~~l^\ast{:}\Tvec(\Tlabelidx_I)~~l_N{:}\Tlabelidx_I @@ -940,16 +947,9 @@ Such a folded instruction can appear anywhere a regular instruction can. \quad\equiv \\ &\qquad \Tfoldedinstr^\ast~~\text{if}~~\Tlabel &\hspace{-12ex} \Tblocktype~~\Tinstr_1^\ast~~\text{else}~~(\Tinstr_2^\ast)^?~\text{end} \\ & - \text{(}~\text{try}~~\Tlabel~~\Tblocktype~~\text{(}~\text{do} &\hspace{-8ex} \Tinstr_1^\ast~\text{)}~~ - (\text{(}~\text{catch}~~x{:}\Ttagidx_I~~\Tinstr_2^\ast~\text{)})^\ast \\ &\quad - (\text{(}~\text{catch\_all}~~\Tinstr_3^\ast~\text{)})^?~\text{)} - \quad\equiv \\ &\qquad - \text{try}~~\Tlabel~~\Tblocktype~~\Tinstr_1^\ast - &\hspace{-5ex} (\text{catch}~~x{:}\Ttagidx_I~~\Tinstr_2^\ast)^\ast~~(\text{catch\_all}~~\Tinstr_3^\ast)^?~~\text{end} \\ & - \text{(}~\text{try}~~\Tlabel~~\Tblocktype~~\text{(}~\text{do} &\hspace{-8ex} \Tinstr^\ast~\text{)}~~ - \text{(}~\text{delegate}~~l{:}\Tlabelidx~~\text{)}~\text{)} + \text{(}~\text{try\_table}~~\Tlabel~~\Tblocktype~~\Tcatch^\ast~~\Tinstr^\ast~\text{)} \quad\equiv \\ &\qquad - \text{try}~~\Tlabel~~\Tblocktype~~\Tinstr^\ast &\hspace{-5ex} \text{delegate}~~l{:}\Tlabelidx \\ + \text{try\_table}~~\Tlabel~~\Tblocktype~~\Tcatch^\ast~~\Tinstr^\ast~~\text{end} \\ \end{array} diff --git a/document/core/text/modules.rst b/document/core/text/modules.rst index 9f354cec..5bf0e209 100644 --- a/document/core/text/modules.rst +++ b/document/core/text/modules.rst @@ -17,6 +17,7 @@ Modules .. _text-funcidx: .. _text-tableidx: .. _text-memidx: +.. _text-tagidx: .. _text-elemidx: .. _text-dataidx: .. _text-globalidx: diff --git a/document/core/text/types.rst b/document/core/text/types.rst index 7705cc57..685a3657 100644 --- a/document/core/text/types.rst +++ b/document/core/text/types.rst @@ -48,9 +48,11 @@ Reference Types \begin{array}{llcll@{\qquad\qquad}l} \production{reference type} & \Treftype &::=& \text{funcref} &\Rightarrow& \FUNCREF \\ &&|& + \text{exnref} &\Rightarrow& \EXNREF \\ &&|& \text{externref} &\Rightarrow& \EXTERNREF \\ \production{heap type} & \Theaptype &::=& \text{func} &\Rightarrow& \FUNCREF \\ &&|& + \text{exn} &\Rightarrow& \EXNREF \\ &&|& \text{extern} &\Rightarrow& \EXTERNREF \\ \end{array} diff --git a/document/core/util/macros.def b/document/core/util/macros.def index eae5e93f..f2e71072 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -176,6 +176,7 @@ .. Types, terminals .. |to| mathdef:: \xref{syntax/types}{syntax-functype}{\rightarrow} +.. |toF| mathdef:: \xref{syntax/types}{syntax-functype}{\rightarrow} .. |I8| mathdef:: \xref{exec/runtime}{syntax-storagetype}{\K{i8}} .. |I16| mathdef:: \xref{exec/runtime}{syntax-storagetype}{\K{i16}} @@ -194,6 +195,7 @@ .. |FUNCREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{funcref}} .. |EXTERNREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{externref}} +.. |EXNREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{exnref}} .. |MVAR| mathdef:: \xref{syntax/types}{syntax-mut}{\K{var}} .. |MCONST| mathdef:: \xref{syntax/types}{syntax-mut}{\K{const}} @@ -377,12 +379,13 @@ .. |RETURN| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{return}} .. |CALL| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{call}} .. |CALLINDIRECT| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{call\_indirect}} -.. |TRY| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{try}} +.. |TRYTABLE| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{try\_table}} .. |CATCH| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{catch}} +.. |CATCHREF| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{catch\_ref}} .. |CATCHALL| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{catch\_all}} -.. |DELEGATE| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{delegate}} +.. |CATCHALLREF| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{catch\_all\_ref}} .. |THROW| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{throw}} -.. |RETHROW| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{rethrow}} +.. |THROWREF| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{throw\_ref}} .. |DROP| mathdef:: \xref{syntax/instructions}{syntax-instr-parametric}{\K{drop}} .. |SELECT| mathdef:: \xref{syntax/instructions}{syntax-instr-parametric}{\K{select}} @@ -560,6 +563,7 @@ .. |blocktype| mathdef:: \xref{syntax/instructions}{syntax-blocktype}{\X{blocktype}} .. |instr| mathdef:: \xref{syntax/instructions}{syntax-instr}{\X{instr}} +.. |catch| mathdef:: \xref{syntax/instructions}{syntax-catch}{\X{catch}} .. |expr| mathdef:: \xref{syntax/instructions}{syntax-expr}{\X{expr}} @@ -689,6 +693,7 @@ .. |Bblocktype| mathdef:: \xref{binary/instructions}{binary-blocktype}{\B{blocktype}} .. |Binstr| mathdef:: \xref{binary/instructions}{binary-instr}{\B{instr}} +.. |Bcatch| mathdef:: \xref{binary/instructions}{binary-catch}{\B{catch}} .. |Bexpr| mathdef:: \xref{binary/instructions}{binary-expr}{\B{expr}} .. |Blaneidx| mathdef:: \xref{binary/instructions}{binary-laneidx}{\B{laneidx}} @@ -861,6 +866,7 @@ .. |Tplaininstr| mathdef:: \xref{text/instructions}{text-plaininstr}{\T{plaininstr}} .. |Tblockinstr| mathdef:: \xref{text/instructions}{text-blockinstr}{\T{blockinstr}} .. |Tfoldedinstr| mathdef:: \xref{text/instructions}{text-foldedinstr}{\T{foldedinstr}} +.. |Tcatch| mathdef:: \xref{text/instructions}{text-catch}{\T{catch}} .. |Texpr| mathdef:: \xref{text/instructions}{text-expr}{\T{expr}} @@ -913,7 +919,6 @@ .. |CLABELS| mathdef:: \xref{valid/conventions}{context}{\K{labels}} .. |CRETURN| mathdef:: \xref{valid/conventions}{context}{\K{return}} .. |CREFS| mathdef:: \xref{valid/conventions}{context}{\K{refs}} -.. |LCATCH| mathdef:: \xref{valid/conventions}{context}{\K{catch}} .. Contexts, non-terminals @@ -933,6 +938,7 @@ .. |vdashinstr| mathdef:: \xref{valid/instructions}{valid-instr}{\vdash} .. |vdashinstrseq| mathdef:: \xref{valid/instructions}{valid-instr-seq}{\vdash} +.. |vdashcatch| mathdef:: \xref{valid/instructions}{valid-catch}{\vdash} .. |vdashexpr| mathdef:: \xref{valid/instructions}{valid-expr}{\vdash} .. |vdashexprconst| mathdef:: \xref{valid/instructions}{valid-constant}{\vdash} .. |vdashinstrconst| mathdef:: \xref{valid/instructions}{valid-constant}{\vdash} @@ -994,6 +1000,7 @@ .. |globaladdr| mathdef:: \xref{exec/runtime}{syntax-globaladdr}{\X{globaladdr}} .. |elemaddr| mathdef:: \xref{exec/runtime}{syntax-elemaddr}{\X{elemaddr}} .. |dataaddr| mathdef:: \xref{exec/runtime}{syntax-dataaddr}{\X{dataaddr}} +.. |exnaddr| mathdef:: \xref{exec/runtime}{syntax-exnaddr}{\X{exnaddr}} .. |externaddr| mathdef:: \xref{exec/runtime}{syntax-externaddr}{\X{externaddr}} .. Instances, terminals @@ -1022,6 +1029,9 @@ .. |EINAME| mathdef:: \xref{exec/runtime}{syntax-exportinst}{\K{name}} .. |EIVALUE| mathdef:: \xref{exec/runtime}{syntax-exportinst}{\K{value}} +.. |EITAG| mathdef:: \xref{exec/runtime}{syntax-exninst}{\K{tag}} +.. |EIFIELDS| mathdef:: \xref{exec/runtime}{syntax-exninst}{\K{fields}} + .. |EVFUNC| mathdef:: \xref{exec/runtime}{syntax-externval}{\K{func}} .. |EVTABLE| mathdef:: \xref{exec/runtime}{syntax-externval}{\K{table}} .. |EVMEM| mathdef:: \xref{exec/runtime}{syntax-externval}{\K{mem}} @@ -1052,6 +1062,7 @@ .. |eleminst| mathdef:: \xref{exec/runtime}{syntax-eleminst}{\X{eleminst}} .. |datainst| mathdef:: \xref{exec/runtime}{syntax-datainst}{\X{datainst}} .. |exportinst| mathdef:: \xref{exec/runtime}{syntax-exportinst}{\X{exportinst}} +.. |exninst| mathdef:: \xref{exec/runtime}{syntax-exninst}{\X{exninst}} .. |hostfunc| mathdef:: \xref{exec/runtime}{syntax-hostfunc}{\X{hostfunc}} @@ -1074,6 +1085,7 @@ .. |SGLOBALS| mathdef:: \xref{exec/runtime}{syntax-store}{\K{globals}} .. |SELEMS| mathdef:: \xref{exec/runtime}{syntax-store}{\K{elems}} .. |SDATAS| mathdef:: \xref{exec/runtime}{syntax-store}{\K{datas}} +.. |SEXNS| mathdef:: \xref{exec/runtime}{syntax-store}{\K{exns}} .. Store, non-terminals @@ -1094,7 +1106,6 @@ .. |label| mathdef:: \xref{exec/runtime}{syntax-label}{\X{label}} .. |frame| mathdef:: \xref{exec/runtime}{syntax-frame}{\X{frame}} .. |handler| mathdef:: \xref{exec/runtime}{syntax-handler}{\X{handler}} -.. |exn| mathdef:: \xref{exec/runtime}{syntax-exn}{\X{exn}} .. Stack, meta functions @@ -1103,13 +1114,12 @@ .. Administrative Instructions, terminals -.. |REFFUNCADDR| mathdef:: \xref{exec/runtime}{syntax-ref}{\K{ref}} -.. |REFEXTERNADDR| mathdef:: \xref{exec/runtime}{syntax-ref.extern}{\K{ref{.}extern}} +.. |REFFUNCADDR| mathdef:: \xref{exec/runtime}{syntax-ref.func-addr}{\K{ref}} +.. |REFEXTERNADDR| mathdef:: \xref{exec/runtime}{syntax-ref.extern-addr}{\K{ref{.}extern}} +.. |REFEXNADDR| mathdef:: \xref{exec/runtime}{syntax-ref.exn-addr}{\K{ref{.}exn}} .. |TRAP| mathdef:: \xref{exec/runtime}{syntax-trap}{\K{trap}} .. |INVOKE| mathdef:: \xref{exec/runtime}{syntax-invoke}{\K{invoke}} -.. |THROWadm| mathdef:: \xref{exec/runtime}{syntax-throwadm}{\K{throw}} -.. |HANDLERadm| mathdef:: \xref{exec/runtime}{syntax-handleradm}{\K{handler}} -.. |CAUGHTadm| mathdef:: \xref{exec/runtime}{syntax-caughtadm}{\K{caught}} +.. |HANDLER| mathdef:: \xref{exec/runtime}{syntax-handler}{\K{handler}} .. Values & Results, non-terminals @@ -1270,7 +1280,7 @@ .. |vdashadmininstr| mathdef:: \xref{appendix/properties}{valid-instr-admin}{\vdash} -.. |vdashval| mathdef:: \xref{appendix/properties}{valid-val}{\vdash} +.. |vdashval| mathdef:: \xref{exec/modules}{valid-val}{\vdash} .. |vdashresult| mathdef:: \xref{appendix/properties}{valid-result}{\vdash} .. |vdashfuncinst| mathdef:: \xref{appendix/properties}{valid-funcinst}{\vdash} @@ -1280,6 +1290,7 @@ .. |vdashglobalinst| mathdef:: \xref{appendix/properties}{valid-globalinst}{\vdash} .. |vdasheleminst| mathdef:: \xref{appendix/properties}{valid-eleminst}{\vdash} .. |vdashdatainst| mathdef:: \xref{appendix/properties}{valid-datainst}{\vdash} +.. |vdashexninst| mathdef:: \xref{appendix/properties}{valid-exninst}{\vdash} .. |vdashexportinst| mathdef:: \xref{appendix/properties}{valid-exportinst}{\vdash} .. |vdashmoduleinst| mathdef:: \xref{appendix/properties}{valid-moduleinst}{\vdash} @@ -1295,6 +1306,7 @@ .. |vdashglobalinstextends| mathdef:: \xref{appendix/properties}{extend-globalinst}{\vdash} .. |vdasheleminstextends| mathdef:: \xref{appendix/properties}{extend-eleminst}{\vdash} .. |vdashdatainstextends| mathdef:: \xref{appendix/properties}{extend-datainst}{\vdash} +.. |vdashexninstextends| mathdef:: \xref{appendix/properties}{extend-exninst}{\vdash} .. |vdashstoreextends| mathdef:: \xref{appendix/properties}{extend-store}{\vdash} diff --git a/document/core/valid/conventions.rst b/document/core/valid/conventions.rst index fdcf5e96..544a8133 100644 --- a/document/core/valid/conventions.rst +++ b/document/core/valid/conventions.rst @@ -26,7 +26,6 @@ The skeleton of a sound and complete algorithm for type-checking instruction seq .. index:: ! context, function type, table type, memory type, tag type, global type, value type, result type, index space, module, function, tag, label type .. _context: -.. _syntax-labeltype: Contexts ~~~~~~~~ @@ -43,7 +42,7 @@ which collects relevant information about the surrounding :ref:`module ` accessible from the current position, represented by their :ref:`result type `. * *Return*: the return type of the current function, represented as an optional result type that is absent when no return is allowed, as in free-standing expressions. * *References*: the list of :ref:`function indices ` that occur in the module outside functions and can hence be used to form references inside them. @@ -56,19 +55,18 @@ More concretely, contexts are defined as :ref:`records ` :math: .. math:: \begin{array}{llll} - \production{labeltype} & \labeltype & ::= & \LCATCH^?~\resulttype\\ \production{context} & C &::=& \begin{array}[t]{l@{~}ll} \{ & \CTYPES & \functype^\ast, \\ & \CFUNCS & \functype^\ast, \\ & \CTABLES & \tabletype^\ast, \\ & \CMEMS & \memtype^\ast, \\ - & \CTAGS & \tagtype^\ast, \\ + & \CTAGS & \tagtype^\ast, \\ & \CGLOBALS & \globaltype^\ast, \\ & \CELEMS & \reftype^\ast, \\ & \CDATAS & {\ok}^\ast, \\ & \CLOCALS & \valtype^\ast, \\ - & \CLABELS & \labeltype^\ast, \\ + & \CLABELS & \resulttype^\ast, \\ & \CRETURN & \resulttype^?, \\ & \CREFS & \funcidx^\ast ~\} \\ \end{array} diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index c6b83eee..2b9a937e 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -1234,7 +1234,7 @@ Memory Instructions } -.. index:: control instructions, structured control, label, block, branch, block type, label index, label type, function index, type index, tag index, vector, polymorphism, context +.. index:: control instructions, structured control, label, block, branch, block type, label index, result type, function index, type index, tag index, vector, polymorphism, context pair: validation; instruction single: abstract syntax; instruction .. _valid-label: @@ -1281,7 +1281,7 @@ Control Instructions * The :ref:`block type ` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. * Under context :math:`C'`, the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. @@ -1308,7 +1308,7 @@ Control Instructions * The :ref:`block type ` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`[t_1^\ast]` prepended to the |CLABELS| vector. +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_1^\ast]` prepended to the |CLABELS| vector. * Under context :math:`C'`, the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. @@ -1335,7 +1335,7 @@ Control Instructions * The :ref:`block type ` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. * Under context :math:`C'`, the instruction sequence :math:`\instr_1^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. @@ -1361,86 +1361,121 @@ Control Instructions -.. _valid-try-catch: +.. _valid-try_table: -:math:`\TRY~\blocktype~\instr_1^\ast~(\CATCH~x~\instr_2^\ast)^\ast~(\CATCHALL~\instr_3^\ast)^?~\END` -.................................................................................................... +:math:`\TRYTABLE~\blocktype~\catch^\ast~\instr^\ast~\END` +......................................................... * The :ref:`block type ` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. +* For every :ref:`catch clause ` :math:`\catch_i` in :math:`\catch^\ast`, :math:`\catch_i` must be :ref:`valid `. + +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. * Under context :math:`C'`, - the instruction sequence :math:`\instr_1^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. + the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. -* Let :math:`C''` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`\LCATCH~[t_2^\ast]` prepended to the |CLABELS| vector. +* Then the compound instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`. -* For every :math:`x_i` and :math:`\instr_{2i}^\ast` in :math:`(\CATCH~x~\instr_2^\ast)^\ast`: - * The tag :math:`C.\CTAGS[x_i]` must be defined in the context :math:`C`. +.. math:: + \frac{ + \begin{array}{c} + C \vdashblocktype \blocktype : [t_1^\ast] \to [t_2^\ast] + \qquad + (C \vdashcatch \catch \ok)^\ast + \qquad + C,\CLABELS\,[t_2^\ast] \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_2^\ast] \\ + \end{array} + }{ + C \vdashinstr \TRYTABLE~\blocktype~\catch^\ast~\instr^\ast~\END : [t_1^\ast] \to [t_2^\ast] + } - * Let :math:`[t_{3i}^\ast] \to [t_{4i}^\ast]` be the :ref:`tag type ` :math:`C.\CTAGS[x_i]`. +.. note:: + The :ref:`notation ` :math:`C,\CLABELS\,[t^\ast]` inserts the new label type at index :math:`0`, shifting all others. - * The :ref:`result type ` :math:`[t_{4i}^\ast]` must be empty. - * Under context :math:`C''`, - the instruction sequence :math:`\instr_{2i}^\ast` must be :ref:`valid ` with type :math:`[t_{3i}^\ast] \to [t_2^\ast]`. +.. _valid-catch: -* If :math:`(\CATCHALL~\instr_3^\ast)^?` is not empty, then: +:math:`\CATCH~x~l` +.................. - * Under context :math:`C''`, - the instruction sequence :math:`\instr_3^\ast` must be :ref:`valid ` with type :math:`[] \to [t_2^\ast]`. +* The tag :math:`C.\CTAGS[x]` must be defined in the context. -* Then the compound instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`. +* Let :math:`[t^\ast] \to [{t'}^\ast]` be 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]`. + +* Then the catch clause is valid. .. math:: \frac{ - \begin{array}{c} - C \vdashblocktype \blocktype : [t_1^\ast] \to [t_2^\ast] + C.\CTAGS[x] = [t^\ast] \toF [] \qquad - C,\CLABELS\,[t_2^\ast] \vdashinstrseq \instr_1^\ast : [t_1^\ast] \to [t_2^\ast] \\ - (C.\CTAGS[x] = [t^\ast] \to [] \\ - C,\CLABELS\,(\LCATCH~[t_2^\ast]) \vdashinstrseq \instr_2^\ast : [t^\ast] \to [t_2^\ast])^\ast \\ - (C,\CLABELS\,(\LCATCH~[t_2^\ast]) \vdashinstrseq \instr_3^\ast : [] \to [t_2^\ast])^? - \end{array} + C.\CLABELS[l] = [t^\ast] }{ - C \vdashinstr \TRY~\blocktype~\instr_1^\ast~(\CATCH~x~\instr_2^\ast)^\ast~(\CATCHALL~\instr_3^\ast)^?~\END : [t_1^\ast] \to [t_2^\ast] + C \vdashcatch \CATCH~x~l \ok } +:math:`\CATCHREF~x~l` +..................... -.. note:: - The :ref:`notation ` :math:`C,\CLABELS\,(\LCATCH^?~[t^\ast])` inserts the new label type at index :math:`0`, shifting all others. - +* The tag :math:`C.\CTAGS[x]` must be defined in the context. -.. _valid-try-delegate: +* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`tag type ` :math:`C.\CTAGS[x]`. -:math:`\TRY~\blocktype~\instr^\ast~\DELEGATE~l` -............................................... +* The :ref:`result type ` :math:`[{t'}^\ast]` must be empty. * The label :math:`C.\CLABELS[l]` must be defined in the context. -* The :ref:`block type ` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. +* The :ref:`result type ` :math:`[t^\ast]` must be the same as :math:`C.\CLABELS[l]` with |EXNREF| appended. -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. +* Then the catch clause is valid. -* Under context :math:`C'`, - the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. +.. math:: + \frac{ + C.\CTAGS[x] = [t^\ast] \toF [] + \qquad + C.\CLABELS[l] = [t^\ast~\EXNREF] + }{ + C \vdashcatch \CATCHREF~x~l \ok + } -* Then the compound instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`. +:math:`\CATCHALL~l` +................... + +* The label :math:`C.\CLABELS[l]` must be defined in the context. + +* The :ref:`result type ` :math:`C.\CLABELS[l]` must be empty. + +* Then the catch clause is valid. .. math:: \frac{ - C \vdashblocktype \blocktype : [t_1^\ast] \to [t_2^\ast] - \qquad - C,\CLABELS\,[t_2^\ast] \vdashinstrseq \instr^\ast : [t_1^\ast]\to[t_2^\ast] - \qquad - C.\CLABELS[l] = [t_0^\ast] + C.\CLABELS[l] = [] }{ - C \vdashinstrseq \TRY~\blocktype~\instr^\ast~\DELEGATE~l : [t_1^\ast]\to[t_2^\ast] + C \vdashcatch \CATCHALL~l \ok } -.. note:: - The :ref:`label index ` space in the :ref:`context ` :math:`C` contains the most recent label first, so that :math:`C.\CLABELS[l]` performs a relative lookup as expected. +:math:`\CATCHALLREF~l` +...................... + +* The label :math:`C.\CLABELS[l]` must be defined in the context. + +* The :ref:`result type ` :math:`C.\CLABELS[l] must be :math:`[\EXNREF]`. + +* Then the catch clause is valid. + +.. math:: + \frac{ + C.\CLABELS[l] = [\EXNREF] + }{ + C \vdashcatch \CATCHALLREF~l \ok + } .. _valid-throw: @@ -1450,7 +1485,9 @@ Control Instructions * The tag :math:`C.\CTAGS[x]` must be defined in the context. -* Let :math:`[t^\ast] \to []` be its :ref:`tag type `. +* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`tag type ` :math:`C.\CTAGS[x]`. + +* The :ref:`result type ` :math:`[{t'}^\ast]` must be empty. * Then the instruction is valid with type :math:`[t_1^\ast t^\ast] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. @@ -1458,7 +1495,7 @@ Control Instructions \frac{ C.\CTAGS[x] = [t^\ast] \to [] }{ - C \vdashinstr \THROW~x : [t_1^\ast t^\ast] \to [t_2^\ast] + C \vdashinstr \THROW~x : [t_1^\ast~t^\ast] \to [t_2^\ast] } @@ -1466,30 +1503,23 @@ Control Instructions The |THROW| instruction is :ref:`stack-polymorphic `. -.. _valid-rethrow: - -:math:`\RETHROW~l` -.................. - -* The label :math:`C.\CLABELS[l]` must be defined in the context. - -* Let :math:`(\LCATCH^?~[t^\ast])` be the :ref:`label type ` :math:`C.\CLABELS[l]`. +.. _valid-throw_ref: -* The |LCATCH| must be present in the :ref:`label type ` :math:`C.\CLABELS[l]`. +:math:`\THROWREF` +................. -* Then the instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. +* The instruction is valid with type :math:`[t_1^\ast~\EXNREF] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. .. math:: \frac{ - C.\CLABELS[l] = \LCATCH~[t^\ast] }{ - C \vdashinstr \RETHROW~l : [t_1^\ast] \to [t_2^\ast] + C \vdashinstr \THROWREF : [t_1^\ast~\EXNREF] \to [t_2^\ast] } .. note:: - The |RETHROW| instruction is :ref:`stack-polymorphic `. + The |THROWREF| instruction is :ref:`stack-polymorphic `. @@ -1500,13 +1530,13 @@ Control Instructions * The label :math:`C.\CLABELS[l]` must be defined in the context. -* Let :math:`\LCATCH^?~[t^\ast]` be the :ref:`label type ` :math:`C.\CLABELS[l]`. +* Let :math:`[t^\ast]` be the :ref:`result type ` :math:`C.\CLABELS[l]`. * Then the instruction is valid with type :math:`[t_1^\ast~t^\ast] \to [t_2^\ast]`, for any sequences of :ref:`operand types ` :math:`t_1^\ast` and :math:`t_2^\ast`. .. math:: \frac{ - C.\CLABELS[l] = \LCATCH^?~[t^\ast] + C.\CLABELS[l] = [t^\ast] }{ C \vdashinstr \BR~l : [t_1^\ast~t^\ast] \to [t_2^\ast] } @@ -1524,13 +1554,13 @@ Control Instructions * The label :math:`C.\CLABELS[l]` must be defined in the context. -* Let :math:`\LCATCH^?~[t^\ast]` be the :ref:`label type ` :math:`C.\CLABELS[l]`. +* Let :math:`[t^\ast]` be the :ref:`result type ` :math:`C.\CLABELS[l]`. * Then the instruction is valid with type :math:`[t^\ast~\I32] \to [t^\ast]`. .. math:: \frac{ - C.\CLABELS[l] = \LCATCH^?~[t^\ast] + C.\CLABELS[l] = [t^\ast] }{ C \vdashinstr \BRIF~l : [t^\ast~\I32] \to [t^\ast] } diff --git a/document/core/valid/modules.rst b/document/core/valid/modules.rst index 16f7871a..8404d2fd 100644 --- a/document/core/valid/modules.rst +++ b/document/core/valid/modules.rst @@ -29,7 +29,7 @@ Functions :math:`\func` are classified by :ref:`function types * |CLOCALS| set to the sequence of :ref:`value types ` :math:`t_1^\ast~t^\ast`, concatenating parameters and locals, - * |CLABELS| set to the singular sequence containing only :ref:`label type ` :math:`[t_2^\ast]`. + * |CLABELS| set to the singular sequence containing only :ref:`result type ` :math:`[t_2^\ast]`. * |CRETURN| set to the :ref:`result type ` :math:`[t_2^\ast]`. diff --git a/document/index.html b/document/index.html index 37eed976..79a2a0cf 100644 --- a/document/index.html +++ b/document/index.html @@ -49,19 +49,32 @@

Embedder specifications

  • 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.

  • Web Embedding: defines extensions to the JavaScript API made available specifically in web browsers, in particular, an interface for streaming compilation and instantiation from origin-bound Response types.

+

Legacy Extensions

+ +

Define extensions that are deprecated, but may still be in use.

+ +
    +
  • Legacy Exception Handling: defines additional instructions for exception handling that may still be available in some engines and tools, specifically web browsers.

    + +
  • +
+ +

Source for these documents is available here. diff --git a/document/js-api/index.bs b/document/js-api/index.bs index e7db1407..1519c049 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -111,6 +111,7 @@ urlPrefix: https://webassembly.github.io/exception-handling/core/; spec: WebAsse url: syntax/types.html#syntax-reftype text: reftype text: funcref + text: exnref text: externref url: syntax/values.html#syntax-float text: +∞ @@ -804,6 +805,9 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address The get(|index|) method, when invoked, performs the following steps: 1. Let |tableaddr| be **this**.\[[Table]]. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (limits, |elementType|) be [=table_type=](|store|, |tableaddr|). + 1. If |elementType| is [=exnref=], + 1. [=Throw=] a {{TypeError}} exception. 1. Let |result| be [=table_read=](|store|, |tableaddr|, |index|). 1. If |result| is [=error=], throw a {{RangeError}} exception. 1. Return [=ToJSValue=](|result|). @@ -812,12 +816,14 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address

The set(|index|, |value|) method, when invoked, performs the following steps: 1. Let |tableaddr| be **this**.\[[Table]]. - 1. Let (limits, |elementType|) be [=table_type=](|tableaddr|). + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (limits, |elementType|) be [=table_type=](|store|, |tableaddr|). + 1. If |elementType| is [=exnref=], + 1. [=Throw=] a {{TypeError}} exception. 1. If |value| is missing, 1. Let |ref| be [=DefaultValue=](|elementType|). 1. Otherwise, 1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|). - 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let |store| be [=table_write=](|store|, |tableaddr|, |index|, |ref|). 1. If |store| is [=error=], throw a {{RangeError}} exception. 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. @@ -904,7 +910,7 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each The Global(|descriptor|, |v|) constructor, when invoked, performs the following steps: 1. Let |mutable| be |descriptor|["mutable"]. 1. Let |valuetype| be [=ToValueType=](|descriptor|["value"]). - 1. If |valuetype| is [=v128=], + 1. If |valuetype| is [=v128=] or [=exnref=], 1. Throw a {{TypeError}} exception. 1. If |v| is missing, 1. Let |value| be [=DefaultValue=](|valuetype|). @@ -922,7 +928,7 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each 1. Let |store| be the current agent's [=associated store=]. 1. Let |globaladdr| be |global|.\[[Global]]. 1. Let |globaltype| be [=global_type=](|store|, |globaladdr|). - 1. If |globaltype| is of the form mut [=v128=], throw a {{TypeError}}. + 1. If |globaltype| is of the form mut |valuetype| where |valuetype| is [=v128=] or [=exnref=], throw a {{TypeError}}. 1. Let |value| be [=global_read=](|store|, |globaladdr|). 1. Return [=ToJSValue=](|value|).
@@ -935,7 +941,7 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each 1. Let |store| be the current agent's [=associated store=]. 1. Let |globaladdr| be **this**.\[[Global]]. 1. Let |mut| |valuetype| be [=global_type=](|store|, |globaladdr|). - 1. If |valuetype| is [=v128=], throw a {{TypeError}}. + 1. If |valuetype| is [=v128=] or [=exnref=], throw a {{TypeError}}. 1. If |mut| is [=const=], throw a {{TypeError}}. 1. Let |value| be [=ToWebAssemblyValue=](**the given value**, |valuetype|). 1. Let |store| be [=global_write=](|store|, |globaladdr|, |value|). @@ -994,7 +1000,7 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [ 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let |functype| be [=func_type=](|store|, |funcaddr|). 1. Let [|parameters|] → [|results|] be |functype|. - 1. If |parameters| or |results| contain [=v128=], throw a {{TypeError}}. + 1. If |parameters| or |results| contain [=v128=] or [=exnref=], throw a {{TypeError}}. Note: the above error is thrown each time the \[[Call]] method is invoked. 1. Let |args| be « ». @@ -1032,7 +1038,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not To run a host function from the JavaScript object |func|, type |functype|, and [=list=] of [=WebAssembly values=] |arguments|, perform the following steps: 1. Let [|parameters|] → [|results|] be |functype|. - 1. If |parameters| or |results| contain [=v128=], throw a {{TypeError}}. + 1. If |parameters| or |results| contain [=v128=] or [=exnref=], throw a {{TypeError}}. 1. Let |jsArguments| be « ». 1. [=list/iterate|For each=] |arg| of |arguments|, 1. [=list/Append=] [=!=] [=ToJSValue=](|arg|) to |jsArguments|. @@ -1086,6 +1092,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not The algorithm ToJSValue(|w|) coerces a [=WebAssembly value=] to a JavaScript value by performing the following steps: 1. Assert: |w| is not of the form [=v128.const=] v128. +1. Assert: |w| is not of the form [=ref.exn=] exnaddr. 1. If |w| is of the form [=i64.const=] |i64|, 1. Let |v| be [=signed_64=](|i64|). 1. Return [=ℤ=](|v| interpreted as a mathematical value). @@ -1119,6 +1126,7 @@ For retrieving an extern value from an [=extern address=] |externaddr The algorithm ToWebAssemblyValue(|v|, |type|) coerces a JavaScript value to a [=WebAssembly value=] by performing the following steps: 1. Assert: |type| is not [=v128=]. +1. Assert: |type| is not [=exnref=]. 1. If |type| is [=i64=], 1. Let |i64| be [=?=] [$ToBigInt64$](|v|). 1. Return [=i64.const=] |i64|. diff --git a/document/legacy/exceptions/.gitignore b/document/legacy/exceptions/.gitignore new file mode 100644 index 00000000..b932ec28 --- /dev/null +++ b/document/legacy/exceptions/.gitignore @@ -0,0 +1,3 @@ +_build +_static +document/*.pyc diff --git a/document/legacy/exceptions/LICENSE b/document/legacy/exceptions/LICENSE new file mode 100644 index 00000000..795b406e --- /dev/null +++ b/document/legacy/exceptions/LICENSE @@ -0,0 +1,50 @@ +W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE + +This work is being provided by the copyright holders under the following +license. + + +LICENSE + +By obtaining and/or copying this work, you (the licensee) agree that you have +read, understood, and will comply with the following terms and conditions. + +Permission to copy, modify, and distribute this work, with or without +modification, for any purpose and without fee or royalty is hereby granted, +provided that you include the following on ALL copies of the work or portions +thereof, including modifications: + +* The full text of this NOTICE in a location viewable to users of the + redistributed or derivative work. + +* Any pre-existing intellectual property disclaimers, notices, or terms and + conditions. If none exist, the W3C Software and Document Short Notice + (https://www.w3.org/Consortium/Legal/copyright-software-short-notice) should + be included. + +* Notice of any changes or modifications, through a copyright statement on the + new code or document such as "This software or document includes material + copied from or derived from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." + + +DISCLAIMERS + +THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS +OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF +MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE +SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, +TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. + +The name and trademarks of copyright holders may NOT be used in advertising or +publicity pertaining to the work without specific, written prior permission. +Title to copyright in this work will at all times remain with copyright +holders. + + +NOTES + +This version: +http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document diff --git a/document/legacy/exceptions/Makefile b/document/legacy/exceptions/Makefile new file mode 100644 index 00000000..56b9560a --- /dev/null +++ b/document/legacy/exceptions/Makefile @@ -0,0 +1,358 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = a4 +BUILDDIR = _build +STATICDIR = _static +DOWNLOADDIR = _download +NAME = WebAssembly + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: usage +usage: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " pdf to make standalone PDF file" + @echo " bikeshed to make a bikeshed wrapped single large HTML file" + @echo " diff to make a diff of the bikeshed HTML file with the latest TR" + @echo " WD-tar generate tar file for updating the Working Draft" + @echo " WD-echidna publish the Working Draft tar file via Echidna" + @echo " all to make all 3" + @echo " publish to make all and push to gh-pages" + @echo " help to see more options" + +.PHONY: help +help: + @echo "Usage: \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " pdf to make standalone PDF file" + @echo " bikeshed to make a bikeshed wrapped single large HTML file" + @echo " all to make all 3" + @echo " publish to make all and push to gh-pages" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " epub3 to make an epub3" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " dummy to check syntax errors of document sources" + +.PHONY: deploy +deploy: + (cd ../..; make dir-core deploy-core) + +.PHONY: publish +publish: clean all deploy + +.PHONY: publish-main +publish-main: clean main bikeshed-keep deploy + +.PHONY: all +all: pdf html bikeshed + +.PHONY: main +main: pdf html + +# Dirty hack to avoid rebuilding the Bikeshed version for every push. +.PHONY: bikeshed-keep +bikeshed-keep: + test -e $(BUILDDIR)/html/bikeshed || \ + wget -r -nH --cut-dirs=2 -P $(BUILDDIR)/html --no-check-certificate \ + https://webassembly.github.io/spec/core/bikeshed || \ + echo Downloaded Bikeshed. + + +GENERATED = appendix/index-instructions.rst +.INTERMEDIATE: $(GENERATED) + +%.rst: %.py + (cd `dirname $@`; ./`basename $^`) + +.PHONY: pdf +pdf: $(GENERATED) latexpdf + mkdir -p $(BUILDDIR)/html/$(DOWNLOADDIR) + ln -f $(BUILDDIR)/latex/$(NAME).pdf $(BUILDDIR)/html/$(DOWNLOADDIR)/$(NAME).pdf + + +.PHONY: clean +clean: + rm -rf $(BUILDDIR) + rm -rf $(STATICDIR) + rm -f $(GENERATED) + +.PHONY: html +html: $(GENERATED) + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + for file in `ls $(BUILDDIR)/html/*.html`; \ + do \ + sed s:BASEDIR:.:g <$$file >$$file.out; \ + mv -f $$file.out $$file; \ + done + for file in `ls $(BUILDDIR)/html/*/*.html`; \ + do \ + sed s:BASEDIR:..:g <$$file >$$file.out; \ + mv -f $$file.out $$file; \ + done + @echo + @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html/." + +.PHONY: dirhtml +dirhtml: $(GENERATED) + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: $(GENERATED) + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: bikeshed +bikeshed: $(GENERATED) + $(SPHINXBUILD) -b singlehtml -c util/bikeshed \ + $(ALLSPHINXOPTS) $(BUILDDIR)/bikeshed_singlehtml + python3 util/bikeshed_fixup.py $(BUILDDIR)/bikeshed_singlehtml/index.html \ + >$(BUILDDIR)/bikeshed_singlehtml/index_fixed.html + @echo ==== Showing contents of _build/bikeshed_singlehtml/index_fixed.html ==== + @head -n10 _build/bikeshed_singlehtml/index_fixed.html + @echo ... skipping $$(expr `cat _build/bikeshed_singlehtml/index_fixed.html | wc -l` - 20) lines ... + @tail -n10 _build/bikeshed_singlehtml/index_fixed.html + @echo + @echo ========================================================================= + mkdir -p $(BUILDDIR)/bikeshed_mathjax/ + bikeshed spec 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 \ + >$(BUILDDIR)/html/bikeshed/index.html + mkdir -p $(BUILDDIR)/html/bikeshed/katex/dist/ + cp -r util/katex/dist/* $(BUILDDIR)/html/bikeshed/katex/dist/ + patch -p0 $(BUILDDIR)/html/bikeshed/katex/dist/katex.css \ + < util/katex_fix.patch + cp $(BUILDDIR)/bikeshed_singlehtml/_static/pygments.css \ + $(BUILDDIR)/html/bikeshed/ + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/html/bikeshed/." + +.PHONY: WD-tar +WD-tar: bikeshed + @echo "Building tar file..." + tar cvf \ + $(BUILDDIR)/WD.tar \ + --transform='s|$(BUILDDIR)/html/bikeshed/||' \ + --transform='s|index.html|Overview.html|' \ + $(BUILDDIR)/html/bikeshed/index.html \ + $(BUILDDIR)/html/bikeshed/pygments.css \ + $(BUILDDIR)/html/bikeshed/katex/dist/katex.css \ + $(BUILDDIR)/html/bikeshed/katex/dist/fonts + @echo "Built $(BUILDDIR)/WD.tar." + +.PHONY: WD-echidna +WD-echidna: WD-tar + @if [ -z $(W3C_USERNAME) ] || \ + [ -z $(W3C_PASSWORD) ] || \ + [ -z $(DECISION_URL) ] ; then \ + echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \ + exit 1; \ + fi + curl 'https://labs.w3.org/echidna/api/request' \ + --user '$(W3C_USERNAME):$(W3C_PASSWORD)' \ + -F "tar=@$(BUILDDIR)/WD.tar" \ + -F "decision=$(DECISION_URL)" | tee $(BUILDDIR)/WD-echidna-id.txt + @echo + @echo "Published working draft. Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" + +.PHONY: diff +diff: bikeshed + @echo "Downloading the old single-file html spec..." + curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/bikeshed/old.html + @echo "Done." + @echo "Diffing new against old (go get a coffee)..." + perl ../util/htmldiff.pl $(BUILDDIR)/html/bikeshed/old.html $(BUILDDIR)/html/bikeshed/index.html $(BUILDDIR)/html/bikeshed/diff.html + @echo "Done. The diff is at $(BUILDDIR)/html/bikeshed/diff.html" + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/WebAssembly.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/WebAssembly.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/WebAssembly" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/WebAssembly" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: epub3 +epub3: + $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 + @echo + @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: $(GENERATED) + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex LATEXMKOPTS=" $(BUILDDIR)/latex/LOG 2>&1 || cat $(BUILDDIR)/latex/LOG + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +.PHONY: dummy +dummy: + $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy + @echo + @echo "Build finished. Dummy builder generates no files." diff --git a/document/legacy/exceptions/README.md b/document/legacy/exceptions/README.md new file mode 100644 index 00000000..06d07523 --- /dev/null +++ b/document/legacy/exceptions/README.md @@ -0,0 +1,25 @@ +# WebAssembly Core Specification Addendum: Legacy Exception Handling + +This is the official WebAssembly "language" specification. + +It uses [Sphinx](http://www.sphinx-doc.org/). To install that: +``` +pip install sphinx +``` +To make HTML (result in `_build/html`): +``` +make html +``` +To make PDF (result in `_build/latex`, requires LaTeX): +``` +make pdf +``` +To make all: +``` +make all +``` +Finally, to make all and update webassembly.github.io/spec with it: +``` +make publish +``` +Please make sure to only use that once a change has approval. diff --git a/document/legacy/exceptions/appendix/index-instructions.py b/document/legacy/exceptions/appendix/index-instructions.py new file mode 100755 index 00000000..ea315469 --- /dev/null +++ b/document/legacy/exceptions/appendix/index-instructions.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 + +# This script generates the `index-instructions.rst` file. The table in that +# file is particularly annoying to update by hand, since the Restructured Text +# format requires the header and columns to line up properly. This is +# especially tedious when merging changes from the upstream spec, or merging a +# proposal back to the spec when it is standardized. + +import os + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +INDEX_INSTRUCTIONS_RST = os.path.join(SCRIPT_DIR, 'index-instructions.rst') + +HEADER = """\ +.. DO NOT EDIT: This file is auto-generated by the index-instructions.py script. + +.. _appendix: + +Appendix +======== + +.. index:: instruction +.. _index-instr: + +Index of Instructions +--------------------- +""" + +FOOTER = """\ + +.. note:: + Multi-byte opcodes are given with the shortest possible encoding in the table. + However, what is following the first byte is actually a :ref:`u32 ` with variable-length encoding + and consequently has multiple possible representations.\ +""" + +COLUMNS = [ + 'Instruction', + 'Binary Opcode', + 'Type', + 'Validation', + 'Execution', +] + + +def MathWrap(s, default=''): + if s is None: + return default + else: + return f':math:`{s}`' + + +def RefWrap(s, kind): + if s is None: + return '' + else: + return f':ref:`{kind} <{s}>`' + + +def Instruction(name, opcode, type=None, validation=None, execution=None, operator=None, validation2=None, execution2=None): + if operator: + execution_str = ', '.join([RefWrap(execution, 'execution'), + RefWrap(operator, 'operator')]) + elif execution2: + execution_str = ', '.join([RefWrap(execution, 'execution'), + RefWrap(execution, 'execution')]) + + else: + execution_str = RefWrap(execution, 'execution') + + if validation2: + validation_str = ', '.join([RefWrap(validation, 'validation'), + RefWrap(validation2, 'validation')]) + else: + validation_str = RefWrap(validation, 'validation') + + return ( + MathWrap(name, '(reserved)'), + MathWrap(opcode), + MathWrap(type), + validation_str, + execution_str + ) + + +INSTRUCTIONS = [ + Instruction(r'\TRY~\X{bt}', r'\hex{06}', r'[t_1^\ast] \to [t_2^\ast]', r'valid-try-catch', r'exec-try-catch', None, r'valid-try-delegate', r'exec-try-delegate'), + Instruction(r'\CATCH~x', r'\hex{07}', None, r'valid-try-catch', r'exec-try-catch'), + Instruction(r'\RETHROW~n', r'\hex{09}', r'[t_1^\ast] \to [t_2^\ast]', r'valid-rethrow', r'exec-rethrow'), + Instruction(r'\DELEGATE~l', r'\hex{18}', None, r'valid-try-delegate', r'exec-try-delegate'), + Instruction(r'\CATCHALL', r'\hex{19}', None, r'valid-try-catch', r'exec-try-catch'), +] + + +def ColumnWidth(n): + return max([len(instr[n]) for instr in INSTRUCTIONS]) + +COLUMN_WIDTHS = [ColumnWidth(i) for i in range(len(COLUMNS))] +DIVIDER = ' '.join('=' * width for width in COLUMN_WIDTHS) + +def Row(columns): + return ' '.join(('{:%d}' % COLUMN_WIDTHS[i]).format(column) + for i, column in enumerate(columns)) + +if __name__ == '__main__': + with open(INDEX_INSTRUCTIONS_RST, 'w') as f: + print(HEADER, file=f) + print(DIVIDER, file=f) + print(Row(COLUMNS), file=f) + print(DIVIDER, file=f) + + for instr in INSTRUCTIONS: + print(Row(instr), file=f) + + print(DIVIDER, file=f) + print(FOOTER, file=f) diff --git a/document/legacy/exceptions/binary.rst b/document/legacy/exceptions/binary.rst new file mode 100644 index 00000000..68ea09df --- /dev/null +++ b/document/legacy/exceptions/binary.rst @@ -0,0 +1,31 @@ +.. _binary: + +Binary Format +============= + +.. index:: instruction +.. _binary-instr: + +Instructions +------------ + +.. _binary-instr-control: + +Control Instructions +~~~~~~~~~~~~~~~~~~~~ + +.. _binary-try: +.. _binary-rethrow: + +.. math:: + \begin{array}{llcllll} + \production{instruction} & \Binstr &::=& \dots \\ &&|& + \hex{06}~~\X{bt}{:}\Bblocktype~~(\X{in}_1{:}\Binstr)^\ast~~ + (\hex{07}~~x{:}\Btagidx~~(\X{in}_2{:}\Binstr)^\ast)^\ast~~ + (\hex{19}~~(\X{in}_3{:}\Binstr)^\ast)^?~~\hex{0B} + &\Rightarrow& \TRY~\X{bt}~\X{in}_1^\ast~(\CATCH~x~\X{in}_2^\ast)^\ast~ + (\CATCHALL~\X{in}_3^\ast)^?\END \\ &&|& + \hex{06}~~\X{bt}{:}\Bblocktype~~(\X{in}{:}\Binstr)^\ast~~\hex{18}~~l{:}\Blabelidx + &\Rightarrow& \TRY~\X{bt}~\X{in}^\ast~\DELEGATE~l \\ &&|& + \hex{09}~~l{:}\Blabelidx &\Rightarrow& \RETHROW~l \\ + \end{array} diff --git a/document/legacy/exceptions/conf.py b/document/legacy/exceptions/conf.py new file mode 100644 index 00000000..ebd159bd --- /dev/null +++ b/document/legacy/exceptions/conf.py @@ -0,0 +1,498 @@ +# -*- coding: utf-8 -*- +# +# WebAssembly documentation build configuration file, created by +# sphinx-quickstart on Mon Nov 21 11:32:49 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +import os +import sys +from datetime import date + +pwd = os.path.abspath('.') +sys.path.insert(0, pwd) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +needs_sphinx = '2.3' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx.ext.ifconfig', + 'sphinx.ext.githubpages', + 'util.mathdef', + 'util.pseudo-lexer' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_suffix = ['.rst'] + +# The encoding of source files. +# +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +name = 'WebAssembly' +project = u'WebAssembly' +title = u'WebAssembly Specification Addendum: Legacy Exception Handling' +copyright = u'2023, WebAssembly Community Group' +author = u'WebAssembly Community Group' +editor = u'Andreas Rossberg (editor)' +logo = 'static/webassembly.png' + +# The name of the GitHub repository this resides in +repo = 'spec' + +# The draft version string (clear out for release cuts) +draft = ' (Draft ' + date.today().strftime("%Y-%m-%d") + ')' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'0.1' +# The full version, including alpha/beta/rc tags. +release = version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# +# today = '' +# +# Else, today_fmt is used as the format for a strftime call. +# +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = { + 'logo': logo, + 'logo_name': 'WebAssembly', + 'description': 'WebAssembly Specification', + 'fixed_sidebar': True, + 'sidebar_width': '260px', + 'sidebar_collapse': True, + 'show_powered_by': False, + 'extra_nav_links': { + 'Index': 'BASEDIR/genindex.html', + 'Download as PDF': 'BASEDIR/_download/' + name + '.pdf' + }, +} + +html_sidebars = { + '**': [ + # 'about.html', + 'navigation.html', + # 'relations.html', + 'searchbox.html', + ] +} + + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. +# " v documentation" by default. +# +html_title = project + u' ' + release + +# A shorter title for the navigation bar. Default is the same as html_title. +# +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# +html_logo = logo + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static/custom.css'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# +# html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +# +# html_last_updated_fmt = None + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# +# html_use_smartypants = True + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# +# html_additional_pages = {} + +# If false, no module index is generated. +# +html_domain_indices = False + +# If false, no index is generated. +# +html_use_index = True + +# If true, the index is split into individual pages for each letter. +# +html_split_index = False + +# If true, the reST sources are included in the HTML build as _sources/name. The default is True. +# +html_copy_source = False + +# If true, links to the reST sources are added to the pages. +# +html_show_sourcelink = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# +html_show_sphinx = False + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# +html_show_copyright = True + +# If this is not None, a ‘Last updated on:’ timestamp is inserted at every +# page bottom, using the given strftime() format. +# +html_last_updated_fmt = '%Y-%m-%d' + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# +# html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' +# +# html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +# +# html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +# +# html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +# +htmlhelp_basename = 'WebAssemblydoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('a4paper' or 'letterpaper'). + 'papersize': 'a4paper', + + # The font size ('10pt', '11pt' or '12pt'). + 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # Don't type-set cross references with emphasis. + 'preamble': '\\renewcommand\\sphinxcrossref[1]{#1}\n', + + # Latex figure (float) alignment + 'figure_align': 'htbp', + + # Fancy chapters [Bjarne, Sonny, Lenny, Glenn, Conny, Rejne] + 'fncychap': '\\usepackage[Sonny]{fncychap}', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ( master_doc, + name + '.tex', + title, + author + '\\\\ \\hfill\\large ' + editor, + 'manual' + ), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# +latex_logo = logo + +# For "manual" documents [part, chapter, or section]. +# +latex_toplevel_sectioning = 'section' + +# If true, show page references after internal links. +# +latex_show_pagerefs = False + +# How to show URL addresses after external links [no, footnote, inline]. +# +latex_show_urls = 'footnote' + +# Documents to append as an appendix to all manuals. +# +# latex_appendices = [] + +# It false, will not define \strong, \code, \titleref, \crossref ... but only +# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added +# packages. +# +# latex_keep_old_macro_names = True + +# If false, no module index is generated. +# +latex_domain_indices = False + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ( master_doc, + name, + title, + [author], + 1 + ) +] + +# If true, show URL addresses after external links. +# +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( master_doc, + name, + title, + author, + name, + 'A portable low-level execution format.', + 'Virtual Machine' + ), +] + +# Documents to append as an appendix to all manuals. +# +# texinfo_appendices = [] + +# If false, no module index is generated. +# +texinfo_domain_indices = False + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# +# texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The basename for the epub file. It defaults to the project name. +# epub_basename = project + +# The HTML theme for the epub output. Since the default themes are not +# optimized for small screen space, using the same theme for HTML and epub +# output is usually not wise. This defaults to 'epub', a theme designed to save +# visual space. +# +# epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or 'en' if the language is not set. +# +# epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +# epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +# +# epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +# +# epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# +# epub_pre_files = [] + +# HTML files that should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# +# epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +# +# epub_tocdepth = 3 + +# Allow duplicate toc entries. +# +# epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +# +# epub_tocscope = 'default' + +# Fix unsupported image types using the Pillow. +# +# epub_fix_images = False + +# Scale large images. +# +# epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# +# epub_show_urls = 'inline' + +# If false, no index is generated. +# +# epub_use_index = True + +# Macros +rst_prolog = """ +.. |issuelink| replace:: https://github.com/webassembly/""" + repo + """/issues/ +.. |pagelink| replace:: https://webassembly.github.io/""" + repo + """/core/ +.. include:: /""" + pwd + """/util/macros.def +""" + +# https://www.sphinx-doc.org/en/master/usage/extensions/math.html#confval-mathjax3_config +# https://docs.mathjax.org/en/latest/web/configuration.html#configuration +# https://docs.mathjax.org/en/latest/options/input/tex.html#tex-maxbuffer +mathjax3_config = { + 'tex': { 'maxBuffer': 30*1024 }, +} diff --git a/document/legacy/exceptions/exec.rst b/document/legacy/exceptions/exec.rst new file mode 100644 index 00000000..a935eaca --- /dev/null +++ b/document/legacy/exceptions/exec.rst @@ -0,0 +1,351 @@ +.. _exec: + +Execution +========= + +.. _syntax-runtime: + +Runtime Structure +----------------- + +.. _handler: +.. _stack: + +Stack +~~~~~ + +.. _syntax-handler: + +Exception Handlers +.................. + +Legacy exception handlers are installed by |TRY| instructions. +Instead of branch labels, their catch clauses have instruction blocks associated with them. +Furthermore, a |DELEGATE| handler is associated with a label index to implicitly rewthrow to: + +.. math:: + \begin{array}{llllll} + \production{catch} & \catch &::=& \dots \\ &&|& + \CATCH~\tagidx~\instr^\ast \\ &&|& + \CATCHALL~\tagidx~\instr^\ast \\ &&|& + \DELEGATE~\labelidx \\ + \end{array} + + +.. _syntax-caught: +.. _syntax-instr-admin: + +Administrative Instructions +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Administrative instructions are extended with the |CAUGHT| instruction that models exceptions caught by legacy exception handlers. + +.. math:: + \begin{array}{llcl} + \production{administrative instruction} & \instr &::=& \dots \\ &&|& + \CAUGHT_n\{\exnaddr\}~\instr^\ast~\END \\ + \end{array} + + +.. _syntax-ctxt-block: + +Block Contexts +.............. + +Block contexts are extended to include |CAUGHT| instructions: + +.. math:: + \begin{array}{llll} + \production{block contexts} & \XB^k &::=& \dots \\ &&|& + \CAUGHT_n~\{\exnaddr\}~\XB^k~\END \\ + \end{array} + + +.. _syntax-ctxt-throw: + +Throw Contexts +.............. + +Throw contexts are also extended to include |CAUGHT| instructions: + +.. math:: + \begin{array}{llll} + \production{throw contexts} & \XT &::=& \dots \\ &&|& + \CAUGHT_n\{\exnaddr\}~\XT~\END \\ + \end{array} + + +.. _exec-instr: + +Instructions +------------ + +.. _exec-instr-control: + +Control Instructions +~~~~~~~~~~~~~~~~~~~~ + +.. _exec-try-catch: + +:math:`\TRY~\blocktype~\instr_1^\ast~(\CATCH~x~\instr_2^\ast)^\ast~(\CATCHALL~\instr_3^\ast)^?~\END` +.................................................................................................... + +1. Assert: due to :ref:`validation `, :math:`\expand_F(\blocktype)` is defined. + +2. Let :math:`[t_1^m] \to [t_2^n]` be the :ref:`function type ` :math:`\expand_F(\blocktype)`. + +3. Let :math:`L` be the label whose arity is :math:`n` and whose continuation is the end of the |TRY| instruction. + +4. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. + +5. Pop the values :math:`\val^m` from the stack. + +6. Let :math:`F` be the :ref:`current ` :ref:`frame `. + +7. For each catch clause :math:`(\CATCH~x_i~\instr_{2i}^\ast)` do: + + a. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITAGS[x_i]` exists. + + b. Let :math:`a_i` be the tag address :math:`F.\AMODULE.\MITAGS[x_i]`. + + c. Let :math:`\catch_i` be the catch clause :math:`(\CATCH~a_i~\instr_{2i}^\ast)`. + +8. If there is a catch-all clause :math:`(\CATCHALL~\instr_3^\ast)`, then: + + a. Let :math:`\catch'^?` be the handler :math:`(\CATCHALL~\instr_3^\ast)`. + +9. Else: + + a. Let :math:`\catch'^?` be empty. + +10. Let :math:`\catch^\ast` be the concatenation of :math:`\catch_i` and :math:`\catch'^?`. + +11. :ref:`Enter ` the block :math:`\val^m~\instr_1^\ast` with label :math:`L` and exception handler :math:`\HANDLER_n\{\catch^\ast\}^\ast`. + +.. math:: + ~\\[-1ex] + \begin{array}{l} + F; \val^m~(\TRY~\X{bt}~\instr_1^\ast~(\CATCH~x~\instr_2^\ast)^\ast~(\CATCHALL~\instr_3^\ast)^?~\END + \quad \stepto \\ + \qquad F; \LABEL_n\{\epsilon\}~(\HANDLER_n\{(\CATCH~a_x~\instr_2^\ast)^\ast~(\CATCHALL~\instr_3^\ast)^?\}~\val^m~\instr_1^\ast~\END)~\END \\ + (\iff \expand_F(\X{bt}) = [t_1^m] \to [t_2^n] \land (F.\AMODULE.\MITAGS[x]=a_x)^\ast) + \end{array} + + +.. _exec-try-delegate: + +:math:`\TRY~\blocktype~\instr^\ast~\DELEGATE~l` +............................................... + +1. Assert: due to :ref:`validation `, :math:`\expand_F(\blocktype)` is defined. + +2. Let :math:`[t_1^m] \to [t_2^n]` be the :ref:`function type ` :math:`\expand_F(\blocktype)`. + +3. Let :math:`L` be the label whose arity is :math:`n` and whose continuation is the end of the |TRY| instruction. + +4. Let :math:`H` be the :ref:`exception handler ` :math:`l`, targeting the :math:`l`-th surrounding block. + +5. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. + +6. Pop the values :math:`\val^m` from the stack. + +7. :ref:`Enter ` the block :math:`\val^m~\instr^\ast` with label :math:`L` and exception handler `\HANDLER_n\{\DELEGATE~l\}`. + +.. math:: + ~\\[-1ex] + \begin{array}{lcl} + F; \val^m~(\TRY~\X{bt}~\instr^\ast~\DELEGATE~l) &\stepto& + F; \LABEL_n\{\epsilon\}~(\HANDLER_n\{\DELEGATE~l\}~\val^m~\instr^\ast~\END)~\END \\ + && (\iff \expand_F(\X{bt}) = [t_1^m] \to [t_2^n]) + \end{array} + + +.. _exec-throw_ref: + +: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. + +3. Pop the reference :math:`\reff` from the stack. + +4. If :math:`\reff` is :math:`\REFNULL~\X{ht}`, then: + + a. Trap. + +5. Assert: due to :ref:`validation `, :math:`\reff` is an :ref:`exception reference `. + +6. Let :math:`\REFEXNADDR~\X{ea}` be :math:`\reff`. + +7. 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}]`. + +9. 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: + + 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. + +12. 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. + +14. Pop the exception handler :math:`\HANDLER_n\{\catch^\ast\}` from the stack. + +15. 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: + + a. 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:`exception 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:`exception address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: + + i. Push the values :math:`\X{exn}.\EIFIELDS` to the stack. + + ii. Push the exception reference :math:`\REFEXNADDR~\X{ea}` to the stack. + + iii. Execute the instruction :math:`\BR~l`. + + d. 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: + + i. Push the exception reference :math:`\REFEXNADDR~\X{ea}` to the stack. + + ii. Execute the instruction :math:`\BR~l`. + + f. Else if :math:`\catch_1` is of the form :math:`\CATCH~x~\instr^\ast` and the :ref:`exception address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: + + i. Push the caught exception :math:`\CAUGHT_n\{\X{ea}\}` to the stack. + + ii. Push the values :math:`\X{exn}.\EIFIELDS` to the stack. + + iii. :ref:`Enter ` the catch block :math:`\instr^\ast`. + + g. Else if :math:`\catch_1` is of the form :math:`\CATCHALL~\instr^\ast`, then: + + i. Push the caught exception :math:`\CAUGHT_n\{\X{ea}\}` to the stack. + + ii. :ref:`Enter ` the catch block :math:`\instr^\ast`. + + h. Else if :math:`\catch_1` is of the form :math:`\DELEGATE~l`, then: + + i. Assert: due to :ref:`validation `, the stack contains at least :math:`l` labels. + + ii. Repeat :math:`l` times: + + * While the top of the stack is not a label, do: + + - Pop the top element from the stack. + + iii. Assert: due to :ref:`validation `, the top of the stack now is a label. + + iv. Pop the label from the stack. + + v. Push the exception reference :math:`\REFEXNADDR~\X{ea}` back to the stack. + + vi. Execute the instruction :math:`\THROWREF` again. + + i. Else: + + 1. Push the modified handler :math:`\HANDLER_n\{{\catch'}^\ast\}` back to the stack. + + 2. Push the exception reference :math:`\REFEXNADDR~\X{ea}` back to the stack. + + 3. Execute the instruction :math:`\THROWREF` again. + +.. math:: + ~\\[-1ex] + \begin{array}{rcl} + \dots \\ + \HANDLER_n\{(\CATCH~x~\instr^\ast)~\catch^\ast\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END &\stepto& + \CAUGHT_n\{a\}~\X{exn}.\EIFIELDS~\instr^\ast~\END \\ && + (\begin{array}[t]{@{}r@{~}l@{}} + \iff & \X{exn} = S.\SEXNS[a] \\ + \land & \X{exn}.\EITAG = F.\AMODULE.\MITAGS[x]) \\ + \end{array} \\ + \HANDLER_n\{(\CATCHALL~\instr^\ast)~\catch^\ast\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END &\stepto& + \CAUGHT_n\{a\}~\instr^\ast~\END \\ + \XB^l[\HANDLER_n\{(\DELEGATE~l)~\catch^\ast\}~\XT[(\REFEXNADDR~a)~\THROWREF]~\END] &\stepto& + (\REFEXNADDR~a)~\THROWREF \\ + \end{array} + + +.. _exec-rethrow: + +:math:`\RETHROW~l` +.................. + +1. Assert: due to :ref:`validation `, the stack contains at least :math:`l+1` labels. + +2. Let :math:`L` be the :math:`l`-th label appearing on the stack, starting from the top and counting from zero. + +3. Assert: due to :ref:`validation `, :math:`L` is a catch label, i.e., a label of the form :math:`(\LCATCH~[t^\ast])`, which is a label followed by a caught exception in an active catch clause. + +4. Let :math:`a` be the caught exception address. + +5. Push the value :math:`\REFEXNADDR~a` onto the stack. + +6. Execute the instruction |THROWREF|. + +.. math:: + ~\\[-1ex] + \begin{array}{lclr@{\qquad}} + \CAUGHT_n\{a\}~\XB^l[\RETHROW~l]~\END &\stepto& + \CAUGHT_n\{a\}~\XB^l[(\REFEXNADDR~a)~\THROWREF]~\END \\ + \end{array} + + +.. _exec-caught-enter: + +Entering a catch block +...................... + +1. Jump to the start of the instruction sequence :math:`\instr^\ast`. + + +.. _exec-caught-exit: + +Exiting a catch block +..................... + +When the |END| of a catch block is reached without a jump, thrown exception, or trap, then the following steps are performed. + +1. Let :math:`\val^m` be the values on the top of the stack. + +2. Pop the values :math:`\val^m` from the stack. + +3. Assert: due to :ref:`validation `, a caught exception is now on the top of the stack. + +4. Pop the caught exception from the stack. + +5. Push :math:`\val^m` back to the stack. + +6. Jump to the position after the |END| of the administrative instruction associated with the caught exception. + +.. math:: + \begin{array}{rcl} + \CAUGHT_n\{a\}~\val^m~\END &\stepto& \val^m + \end{array} + +.. note:: + A caught exception can only be rethrown from the scope of the administrative instruction associated with it, i.e., from the scope of the |CATCH| or |CATCHALL| block of a legacy |TRY| instruction. Upon exit from that block, the caught exception is discarded. diff --git a/document/legacy/exceptions/index.rst b/document/legacy/exceptions/index.rst new file mode 100644 index 00000000..b6428fb7 --- /dev/null +++ b/document/legacy/exceptions/index.rst @@ -0,0 +1,22 @@ +WebAssembly Specification Addendum: Legacy Exception Handling +============================================================= + +.. only:: html + + | Release |release| + + | Editor: Andreas Rossberg + + | Latest Draft: |WasmDraft| + | Issue Tracker: |WasmIssues| + +.. toctree:: + :maxdepth: 1 + + intro + syntax + valid + exec + binary + text + appendix/index-instructions diff --git a/document/legacy/exceptions/intro.rst b/document/legacy/exceptions/intro.rst new file mode 100644 index 00000000..b8d8c57c --- /dev/null +++ b/document/legacy/exceptions/intro.rst @@ -0,0 +1,8 @@ +.. _intro: + +Introduction +============ + +This document describes an extension of the official WebAssembly standard +developed by its `W3C Community Group `_ with additional instructions for exception handling. +These instructions were never standardized and are deprecated, but they may still be available in some engines, especially in web browsers. diff --git a/document/legacy/exceptions/static/custom.css b/document/legacy/exceptions/static/custom.css new file mode 100644 index 00000000..33bb863d --- /dev/null +++ b/document/legacy/exceptions/static/custom.css @@ -0,0 +1,78 @@ +a { + color: #004BAB; + text-decoration: none; +} + +a.reference { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px dotted #004BAB; +} + +body { + font-size: 15px; +} + +div.document { width: 1000px; } +div.bodywrapper { margin: 0 0 0 200px; } +div.body { padding: 0 10px 0 10px; } +div.footer { width: 1000px; } + +div.body h1 { font-size: 200%; } +div.body h2 { font-size: 150%; } +div.body h3 { font-size: 120%; } +div.body h4 { font-size: 110%; } + +div.note { + border: 0px; + font-size: 90%; + background-color: #F6F8FF; +} + +div.admonition { + padding: 10px; +} + +div.admonition p.admonition-title { + margin: 0px 0px 0px 0px; + font-size: 100%; + font-weight: bold; +} + +div.math { + background-color: #F0F0F0; + padding: 3px 0 3px 0; + overflow-x: auto; + overflow-y: hidden; +} + +div.relations { + display: block; +} + +div.sphinxsidebar { + z-index: 1; + background: #FFF; + margin-top: -30px; + font-size: 13px; + width: 200px; + height: 100%; +} + +div.sphinxsidebarwrapper p.logo { + padding: 30px 40px 10px 0px; +} + +div.sphinxsidebar h3 { + font-size: 0px; +} + +div.sphinxsidebar a { + border-bottom: 0px; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px dotted; +} diff --git a/document/legacy/exceptions/static/webassembly.png b/document/legacy/exceptions/static/webassembly.png new file mode 100644 index 0000000000000000000000000000000000000000..f9edc61098a6f6957903d4e67d92c81269c22e49 GIT binary patch literal 43955 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 0: + global auxcounter + auxcounter = auxcounter + 1 + name = "\\mathdef%d" % auxcounter + s = "\\def%s#%d{%s}%s" % (name, arity, s, name) + return s + return name + +def replace_mathdefs(doc, s): + if not hasattr(doc, 'mathdefs'): + return s + return def_re.sub(lambda m: lookup_mathdef(doc.mathdefs, m.group(0)), s) + +def ext_math_role(role, raw, text, line, inliner, options = {}, content = []): + text = replace_mathdefs(inliner.document, raw.split('`')[1]) + return [math(raw, text)], [] + +class ExtMathDirective(MathDirective): + def run(self): + doc = self.state.document + for i, s in enumerate(self.content): + self.content[i] = replace_mathdefs(doc, s) + for i, s in enumerate(self.arguments): + self.arguments[i] = replace_mathdefs(doc, s) + return super().run() + +class MathdefDirective(Replace): + def run(self): + name = '\\' + self.state.parent.rawsource.split('|')[1] + name = name.split('#') + if len(name) > 1: + arity = int(name[1]) + else: + arity = 0 + name = name[0] + doc = self.state.document + if not hasattr(doc, 'mathdefs'): + doc.mathdefs = {} + # TODO: we don't ever hit the case where len(self.content) > 1 + for i, s in enumerate(self.content): + self.content[i] = replace_mathdefs(doc, s) + doc.mathdefs[name] = [arity, ''.join(self.content)] + self.content[0] = ':math:`' + self.content[0] + self.content[-1] = self.content[-1] + '`' + return super().run() + +class WebAssemblyHTML5Translator(HTML5Translator): + """ + Customize HTML5Translator. + Convert xref in math and math block nodes to hrefs. + """ + def visit_math(self, node, math_env = ''): + html_transform_math_xref(node) + super().visit_math(node, math_env) + + def visit_math_block(self, node, math_env = ''): + html_transform_math_xref(node) + super().visit_math_block(node, math_env) + +class WebAssemblyLaTeXTranslator(LaTeXTranslator): + """ + Customize LaTeXTranslator. + Convert xref in math and math block nodes to hyperrefs. + """ + def visit_math(self, node): + latex_transform_math_xref(node) + super().visit_math(node) + + def visit_math_block(self, node): + latex_transform_math_xref(node) + super().visit_math_block(node) + +# Setup + +def setup(app): + app.set_translator('html', WebAssemblyHTML5Translator) + app.set_translator('latex', WebAssemblyLaTeXTranslator) + app.add_role('math', ext_math_role) + app.add_directive('math', ExtMathDirective, override = True) + app.add_directive('mathdef', MathdefDirective) diff --git a/document/legacy/exceptions/util/pseudo-lexer.py b/document/legacy/exceptions/util/pseudo-lexer.py new file mode 100644 index 00000000..fd3a251d --- /dev/null +++ b/document/legacy/exceptions/util/pseudo-lexer.py @@ -0,0 +1,32 @@ +from pygments.lexer import RegexLexer +from pygments.token import * +from sphinx.highlighting import lexers + +class PseudoLexer(RegexLexer): + name = 'Pseudo' + aliases = ['pseudo'] + filenames = ['*.pseudo'] + + tokens = { + 'root': [ + (r"(?` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. + +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. + +* Under context :math:`C'`, + the instruction sequence :math:`\instr_1^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. + +* Let :math:`C''` be the same :ref:`context ` as :math:`C`, but with the :ref:`label type ` :math:`\LCATCH~[t_2^\ast]` prepended to the |CLABELS| vector. + +* For every :math:`x_i` and :math:`\instr_{2i}^\ast` in :math:`(\CATCH~x~\instr_2^\ast)^\ast`: + + * The tag :math:`C.\CTAGS[x_i]` must be defined in the context :math:`C`. + + * Let :math:`[t_{3i}^\ast] \to [t_{4i}^\ast]` be the :ref:`tag type ` :math:`C.\CTAGS[x_i]`. + + * The :ref:`result type ` :math:`[t_{4i}^\ast]` must be empty. + + * Under context :math:`C''`, + the instruction sequence :math:`\instr_{2i}^\ast` must be :ref:`valid ` with type :math:`[t_{3i}^\ast] \to [t_2^\ast]`. + +* If :math:`(\CATCHALL~\instr_3^\ast)^?` is not empty, then: + + * Under context :math:`C''`, + the instruction sequence :math:`\instr_3^\ast` must be :ref:`valid ` with type :math:`[] \to [t_2^\ast]`. + +* Then the compound instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`. + +.. math:: + \frac{ + \begin{array}{c} + C \vdashblocktype \blocktype : [t_1^\ast] \to [t_2^\ast] + \qquad + C,\CLABELS\,[t_2^\ast] \vdashinstrseq \instr_1^\ast : [t_1^\ast] \to [t_2^\ast] \\ + (C.\CTAGS[x] = [t^\ast] \to [])^\ast \\ + C,\CLABELS\,(\LCATCH~[t_2^\ast]) \vdashinstrseq \instr_2^\ast : [t^\ast] \to [t_2^\ast])^\ast \\ + (C,\CLABELS\,(\LCATCH~[t_2^\ast]) \vdashinstrseq \instr_3^\ast : [] \to [t_2^\ast])^? + \end{array} + }{ + C \vdashinstr \TRY~\blocktype~\instr_1^\ast~(\CATCH~x~\instr_2^\ast)^\ast~(\CATCHALL~\instr_3^\ast)^?~\END : [t_1^\ast] \to [t_2^\ast] + } + + +.. note:: + The :ref:`notation ` :math:`C,\CLABELS\,(\LCATCH^?~[t^\ast])` inserts the new label type at index :math:`0`, shifting all others. + + +.. _valid-try-delegate: + +:math:`\TRY~\blocktype~\instr^\ast~\DELEGATE~l` +............................................... + +* The label :math:`C.\CLABELS[l]` must be defined in the context. + +* The :ref:`block type ` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. + +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. + +* Under context :math:`C'`, + the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. + +* Then the compound instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`. + +.. math:: + \frac{ + C \vdashblocktype \blocktype : [t_1^\ast] \to [t_2^\ast] + \qquad + C,\CLABELS\,[t_2^\ast] \vdashinstrseq \instr^\ast : [t_1^\ast]\to[t_2^\ast] + \qquad + C.\CLABELS[l] = [t_0^\ast] + }{ + C \vdashinstrseq \TRY~\blocktype~\instr^\ast~\DELEGATE~l : [t_1^\ast]\to[t_2^\ast] + } + +.. note:: + The :ref:`label index ` space in the :ref:`context ` :math:`C` contains the most recent label first, so that :math:`C.\CLABELS[l]` performs a relative lookup as expected. + + +.. _valid-rethrow: + +:math:`\RETHROW~l` +.................. + +* The label :math:`C.\CLABELS[l]` must be defined in the context. + +* Let :math:`(\LCATCH^?~[t^\ast])` be the :ref:`label type ` :math:`C.\CLABELS[l]`. + +* The |LCATCH| must be present in the :ref:`label type ` :math:`C.\CLABELS[l]`. + +* Then the instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. + + +.. math:: + \frac{ + C.\CLABELS[l] = \LCATCH~[t^\ast] + }{ + C \vdashinstr \RETHROW~l : [t_1^\ast] \to [t_2^\ast] + } + + +.. note:: + The |RETHROW| instruction is stack-polymorphic. diff --git a/interpreter/README.md b/interpreter/README.md index 30e044af..e7999896 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -232,8 +232,7 @@ expr: ( loop ? * ) ( if ? ( then * ) ( else * )? ) ( if ? + ( then * ) ( else * )? ) ;; = + (if ? (then *) (else *)?) - ( try ? ( do * ) ( catch * )* ( catch_all * )? ) - ( try ? ( do * ) ( delegate ) ) + ( try_table ? * * ) instr: @@ -242,9 +241,7 @@ instr: loop ? * end ? ;; = (loop ? *) if ? * end ? ;; = (if ? (then *)) if ? * else ? * end ? ;; = (if ? (then *) (else *)) - try ? * (catch ? *)* (catch_all ? *)? end ? - ;; = (try ? (do *) (catch *)* (catch_all *)?) - try ? * delegate ;; = (try ? (do *) (delegate )) + try_table ? * * end ? ;; = (try_table ? * *) op: unreachable @@ -286,7 +283,7 @@ op: ref.is_null ref.func throw - rethrow + throw_ref .const . . @@ -306,6 +303,12 @@ op: .extract_lane(_)? .replace_lane +catch: + catch + catch_ref + catch_all + catch_all_ref + func: ( func ? * * ) ( func ? ( export ) <...> ) ;; = (export (func )) (func ? <...>) ( func ? ( import ) ) ;; = (import (func ? )) diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index 88551a15..a677c405 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -164,6 +164,7 @@ let ref_type s = match s7 s with | -0x10 -> FuncRefType | -0x11 -> ExternRefType + | -0x17 -> ExnRefType | _ -> error s (pos s - 1) "malformed reference type" let value_type s = @@ -264,30 +265,10 @@ let rec instr s = end | 0x05 -> error s pos "misplaced ELSE opcode" - | 0x06 -> - let bt = block_type s in - let es = instr_block s in - let ct = catch_list s in - let ca = - if peek s = Some 0x19 then begin - ignore (byte s); - Some (instr_block s) - end else - None - in - if ct <> [] || ca <> None then begin - end_ s; - try_catch bt es ct ca - end else begin - match op s with - | 0x0b -> try_catch bt es [] None - | 0x18 -> try_delegate bt es (at var s) - | b -> illegal s pos b - end - | 0x07 -> error s pos "misplaced CATCH opcode" + | 0x06 | 0x07 as b -> illegal s pos b | 0x08 -> throw (at var s) - | 0x09 -> rethrow (at var s) - | 0x0a as b -> illegal s pos b + | 0x09 as b -> illegal s pos b + | 0x0a -> throw_ref | 0x0b -> error s pos "misplaced END opcode" | 0x0c -> br (at var s) @@ -309,16 +290,20 @@ let rec instr s = let x = at var s in return_call_indirect x y - | 0x14 | 0x15 | 0x16 | 0x17 as b -> illegal s pos b - - | 0x18 -> error s pos "misplaced DELEGATE opcode" - | 0x19 -> error s pos "misplaced CATCH_ALL opcode" + | 0x14 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19 as b -> illegal s pos b | 0x1a -> drop | 0x1b -> select None | 0x1c -> select (Some (vec value_type s)) - | 0x1d | 0x1e | 0x1f as b -> illegal s pos b + | 0x1d | 0x1e as b -> illegal s pos b + + | 0x1f -> + let bt = block_type s in + let cs = vec (at catch) s in + let es = instr_block s in + end_ s; + try_table bt cs es | 0x20 -> local_get (at var s) | 0x21 -> local_set (at var s) @@ -813,19 +798,25 @@ let rec instr s = and instr_block s = List.rev (instr_block' s []) and instr_block' s es = match peek s with - | None | Some (0x05 | 0x07 | 0x0a | 0x0b | 0x18 | 0x19) -> es + | None | Some (0x05 | 0x0b) -> es | _ -> let pos = pos s in let e' = instr s in instr_block' s ((e' @@ region s pos pos) :: es) -and catch_list s = - if peek s = Some 0x07 then begin - ignore (byte s); - let tag = at var s in - let instrs = instr_block s in - (tag, instrs) :: catch_list s - end else - [] + +and catch s = + match byte s with + | 0x00 -> + let x1 = at var s in + let x2 = at var s in + Operators.catch x1 x2 + | 0x01 -> + let x1 = at var s in + let x2 = at var s in + catch_ref x1 x2 + | 0x02 -> catch_all (at var s) + | 0x03 -> catch_all_ref (at var s) + | _ -> error s (pos s - 1) "malformed catch clause" let const s = let c = at instr_block s in diff --git a/interpreter/binary/encode.ml b/interpreter/binary/encode.ml index 50ee0000..fb93d743 100644 --- a/interpreter/binary/encode.ml +++ b/interpreter/binary/encode.ml @@ -77,7 +77,7 @@ struct let string bs = len (String.length bs); put_string s bs let name n = string (Utf8.encode n) let list f xs = List.iter f xs - let opt f xo = Lib.Option.app f xo + let opt f xo = Option.iter f xo let vec f xs = len (List.length xs); list f xs let gap32 () = let p = pos s in word32 0l; byte 0; p @@ -109,6 +109,7 @@ struct let ref_type = function | FuncRefType -> s7 (-0x10) + | ExnRefType -> s7 (-0x17) | ExternRefType -> s7 (-0x11) let value_type = function @@ -167,20 +168,8 @@ struct op 0x04; block_type bt; list instr es1; if es2 <> [] then op 0x05; list instr es2; end_ () - | TryCatch (bt, es, ct, ca) -> - op 0x06; block_type bt; list instr es; - let catch (tag, es) = - op 0x07; var tag; list instr es - in - list catch ct; - begin match ca with - | None -> () - | Some es -> op 0x19; list instr es - end; - end_ () - | TryDelegate (bt, es, x) -> - op 0x06; block_type bt; list instr es; - op 0x18; var x + | TryTable (bt, cs, es) -> + op 0x1f; block_type bt; vec catch cs; list instr es; end_ () | Br x -> op 0x0c; var x | BrIf x -> op 0x0d; var x | BrTable (xs, x) -> op 0x0e; vec var xs; var x @@ -190,7 +179,7 @@ struct | ReturnCall x -> op 0x12; var x | ReturnCallIndirect (x, y) -> op 0x13; var y; var x | Throw x -> op 0x08; var x - | Rethrow x -> op 0x09; var x + | ThrowRef -> op 0x0a | Drop -> op 0x1a | Select None -> op 0x1b @@ -746,6 +735,13 @@ struct | VecReplace (V128 (F32x4 (V128Op.Replace i))) -> vecop 0x20l; byte i | VecReplace (V128 (F64x2 (V128Op.Replace i))) -> vecop 0x22l; byte i + and catch c = + match c.it with + | Catch (x1, x2) -> byte 0x00; var x1; var x2 + | CatchRef (x1, x2) -> byte 0x01; var x1; var x2 + | CatchAll x -> byte 0x02; var x + | CatchAllRef x -> byte 0x03; var x + let const c = list instr c.it; end_ () diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml index 56834621..13f82a9d 100644 --- a/interpreter/exec/eval.ml +++ b/interpreter/exec/eval.ml @@ -66,13 +66,9 @@ and admin_instr' = | ReturningInvoke of value stack * func_inst | Breaking of int32 * value stack | Throwing of Tag.t * value stack - | Rethrowing of int32 * (admin_instr -> admin_instr) | Label of int32 * instr list * code | Frame of int32 * frame * code - | Catch of int32 * (Tag.t * instr list) list * instr list option * code - | Caught of int32 * Tag.t * value stack * code - | Delegate of int32 * code - | Delegating of int32 * Tag.t * value stack + | Handler of int32 * catch list * code type config = { @@ -237,23 +233,18 @@ let rec step (c : config) : config = let args, vs' = take n vs e.at, drop n vs e.at in vs', [Throwing (t, args) @@ e.at] - | Rethrow x, vs -> - vs, [Rethrowing (x.it, fun e -> e) @@ e.at] + | ThrowRef, Ref (NullRef _) :: vs -> + vs, [Trapping "null exception reference" @@ e.at] - | TryCatch (bt, es', cts, ca), vs -> - let FuncType (ts1, ts2) = block_type frame.inst bt in - let n1 = Lib.List32.length ts1 in - let n2 = Lib.List32.length ts2 in - let args, vs' = take n1 vs e.at, drop n1 vs e.at in - let cts' = List.map (fun (x, es'') -> ((tag frame.inst x), es'')) cts in - vs', [Label (n2, [], ([], [Catch (n2, cts', ca, (args, List.map plain es')) @@ e.at])) @@ e.at] + | ThrowRef, Ref (ExnRef (t, args)) :: vs -> + vs, [Throwing (t, args) @@ e.at] - | TryDelegate (bt, es', x), vs -> + | TryTable (bt, cs, es'), vs -> let FuncType (ts1, ts2) = block_type frame.inst bt in let n1 = Lib.List32.length ts1 in let n2 = Lib.List32.length ts2 in let args, vs' = take n1 vs e.at, drop n1 vs e.at in - vs', [Label (n2, [], ([], [Delegate (x.it, (args, List.map plain es')) @@ e.at])) @@ e.at] + vs', [Handler (n2, cs, (args, [Label (n2, [], ([], List.map plain es')) @@ e.at])) @@ e.at] | Drop, v :: vs' -> vs', [] @@ -655,12 +646,6 @@ let rec step (c : config) : config = | Throwing _, _ -> assert false - | Rethrowing _, _ -> - Crash.error e.at "undefined catch label" - - | Delegating _, _ -> - Crash.error e.at "undefined delegate label" - | Label (n, es0, (vs', [])), vs -> vs' @ vs, [] @@ -682,15 +667,6 @@ let rec step (c : config) : config = | Label (n, es0, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> vs, [Throwing (a, vs0) @@ at] - | Label (n, es0, (vs', {it = Delegating (0l, a, vs0); at} :: es')), vs -> - vs, [Throwing (a, vs0) @@ at] - - | Label (n, es0, (vs', {it = Delegating (k, a, vs0); at} :: es')), vs -> - vs, [Delegating (Int32.sub k 1l, a, vs0) @@ at] - - | Label (n, es0, (vs', {it = Rethrowing (k, cont); at} :: es')), vs -> - vs, [Rethrowing (Int32.sub k 1l, (fun e -> Label (n, es0, (vs', cont e :: es')) @@ e.at)) @@ at] - | Label (n, es0, code'), vs -> let c' = step {c with code = code'} in vs, [Label (n, es0, c'.code) @@ e.at] @@ -715,59 +691,36 @@ let rec step (c : config) : config = let c' = step {frame = frame'; code = code'; budget = c.budget - 1} in vs, [Frame (n, c'.frame, c'.code) @@ e.at] - | Catch (n, cts, ca, (vs', [])), vs -> + | Handler (n, cs, (vs', [])), vs -> vs' @ vs, [] - | Catch (n, cts, ca, (vs', ({it = Trapping _ | Breaking _ | Returning _ | ReturningInvoke _ | Delegating _; at} as e) :: es')), vs -> + | Handler (n, cs, (vs', ({it = Trapping _ | Breaking _ | Returning _ | ReturningInvoke _; at} as e) :: es')), vs -> vs, [e] - | Catch (n, cts, ca, (vs', {it = Rethrowing (k, cont); at} :: es')), vs -> - vs, [Rethrowing (k, (fun e -> Catch (n, cts, ca, (vs', (cont e) :: es')) @@ e.at)) @@ at] - - | Catch (n, (a', es'') :: cts, ca, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> - if a == a' then - vs, [Caught (n, a, vs0, (vs0, List.map plain es'')) @@ at] + | Handler (n, {it = Catch (x1, x2); _} :: cs, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> + if a == tag frame.inst x1 then + vs0 @ vs, [Plain (Br x2) @@ e.at] else - vs, [Catch (n, cts, ca, (vs', {it = Throwing (a, vs0); at} :: es')) @@ e.at] - - | Catch (n, [], Some es'', (vs', {it = Throwing (a, vs0); at} :: es')), vs -> - vs, [Caught (n, a, vs0, (vs0, List.map plain es'')) @@ at] - - | Catch (n, [], None, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> - vs, [Throwing (a, vs0) @@ at] + vs, [Handler (n, cs, (vs', {it = Throwing (a, vs0); at} :: es')) @@ e.at] - | Catch (n, cts, ca, code'), vs -> - let c' = step {c with code = code'} in - vs, [Catch (n, cts, ca, c'.code) @@ e.at] - - | Caught (n, a, vs0, (vs', [])), vs -> - vs' @ vs, [] - - | Caught (n, a, vs0, (vs', ({it = Trapping _ | Breaking _ | Returning _ | ReturningInvoke _ | Throwing _ | Delegating _; at} as e) :: es')), vs -> - vs, [e] - - | Caught (n, a, vs0, (vs', {it = Rethrowing (0l, cont); at} :: es')), vs -> - vs, [Caught (n, a, vs0, (vs', (cont (Throwing (a, vs0) @@ at)) :: es')) @@ e.at] - - | Caught (n, a, vs0, (vs', {it = Rethrowing (k, cont); at} :: es')), vs -> - vs, [Rethrowing (k, (fun e -> Caught (n, a, vs0, (vs', (cont e) :: es')) @@ e.at)) @@ at] - - | Caught (n, a, vs0, code'), vs -> - let c' = step {c with code = code'} in - vs, [Caught (n, a, vs0, c'.code) @@ e.at] + | Handler (n, {it = CatchRef (x1, x2); _} :: cs, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> + if a == tag frame.inst x1 then + Ref (ExnRef (a, vs0)) :: vs0 @ vs, [Plain (Br x2) @@ e.at] + else + vs, [Handler (n, cs, (vs', {it = Throwing (a, vs0); at} :: es')) @@ e.at] - | Delegate (l, (vs', [])), vs -> - vs' @ vs, [] + | Handler (n, {it = CatchAll x; _} :: cs, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> + vs, [Plain (Br x) @@ e.at] - | Delegate (l, (vs', ({it = Trapping _ | Breaking _ | Returning _ | ReturningInvoke _ | Rethrowing _ | Delegating _; at} as e) :: es')), vs -> - vs, [e] + | Handler (n, {it = CatchAllRef x; _} :: cs, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> + Ref (ExnRef (a, vs0)) :: vs, [Plain (Br x) @@ e.at] - | Delegate (l, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> - vs, [Delegating (l, a, vs0) @@ e.at] + | Handler (n, [], (vs', {it = Throwing (a, vs0); at} :: es')), vs -> + vs, [Throwing (a, vs0) @@ at] - | Delegate (l, code'), vs -> + | Handler (n, cs, code'), vs -> let c' = step {c with code = code'} in - vs, [Delegate (l, c'.code) @@ e.at] + vs, [Handler (n, cs, c'.code) @@ e.at] | Invoke func, vs when c.budget = 0 -> Exhaustion.error e.at "call stack exhausted" diff --git a/interpreter/script/js.ml b/interpreter/script/js.ml index a1f27d03..3bcfeb12 100644 --- a/interpreter/script/js.ml +++ b/interpreter/script/js.ml @@ -375,6 +375,7 @@ let assert_return ress ts at = let is_ref_idx = match t with | FuncRefType -> is_funcref_idx + | ExnRefType -> assert false | ExternRefType -> is_externref_idx in [ Call (is_ref_idx @@ at) @@ at; diff --git a/interpreter/script/run.ml b/interpreter/script/run.ml index bb92d0b8..8ca9b7a8 100644 --- a/interpreter/script/run.ml +++ b/interpreter/script/run.ml @@ -409,6 +409,7 @@ let assert_ref_pat r p = match r, p with | r, RefPat r' -> r = r'.it | Instance.FuncRef _, RefTypePat Types.FuncRefType + | Values.ExnRef _, RefTypePat Types.ExnRefType | ExternRef _, RefTypePat Types.ExternRefType -> true | _ -> false diff --git a/interpreter/syntax/ast.ml b/interpreter/syntax/ast.ml index 4a717781..1ba2c2f5 100644 --- a/interpreter/syntax/ast.ml +++ b/interpreter/syntax/ast.ml @@ -185,13 +185,9 @@ and instr' = | Unary of unop (* unary numeric operator *) | Binary of binop (* binary numeric operator *) | Convert of cvtop (* conversion *) - | TryCatch of block_type * instr list * (* try *) - (var * instr list) list * (* catch exception with tag *) - instr list option (* catch_all *) - | TryDelegate of block_type * instr list * (* try *) - var (* delegate to outer handler *) + | TryTable of block_type * catch list * instr list (* handle exceptions *) | Throw of var (* throw exception *) - | Rethrow of var (* rethrow exception *) + | ThrowRef (* rethrow exception *) | VecConst of vec (* constant *) | VecTest of vec_testop (* vector test *) | VecCompare of vec_relop (* vector comparison *) @@ -208,6 +204,13 @@ and instr' = | VecExtract of vec_extractop (* extract lane from vector *) | VecReplace of vec_replaceop (* replace lane in vector *) +and catch = catch' Source.phrase +and catch' = + | Catch of var * var + | CatchRef of var * var + | CatchAll of var + | CatchAllRef of var + (* Globals & Functions *) diff --git a/interpreter/syntax/free.ml b/interpreter/syntax/free.ml index fdc5f363..650fca1c 100644 --- a/interpreter/syntax/free.ml +++ b/interpreter/syntax/free.ml @@ -82,6 +82,10 @@ let rec instr (e : instr) = | Call x | ReturnCall x -> funcs (var x) | CallIndirect (x, y) | ReturnCallIndirect (x, y) -> tables (var x) ++ types (var y) + | Throw x -> tags (var x) + | ThrowRef -> empty + | TryTable (bt, cs, es) -> + block_type bt ++ list catch cs ++ block es | LocalGet x | LocalSet x | LocalTee x -> locals (var x) | GlobalGet x | GlobalSet x -> globals (var x) | TableGet x | TableSet x | TableSize x | TableGrow x | TableFill x -> @@ -100,19 +104,15 @@ let rec instr (e : instr) = memories zero | MemoryInit x -> memories zero ++ datas (var x) | DataDrop x -> datas (var x) - | TryCatch (bt, es, ct, ca) -> - let catch (tag, es) = tags (var tag) ++ block es in - let catch_all = function - | None -> empty - | Some es -> block es in - block es ++ (list catch ct) ++ catch_all ca - | TryDelegate (bt, es, x) -> block es ++ tags (var x) - | Throw x -> tags (var x) - | Rethrow x -> labels (var x) and block (es : instr list) = let free = list instr es in {free with labels = shift free.labels} +and catch (c : catch) = + match c.it with + | Catch (x1, x2) | CatchRef (x1, x2) -> tags (var x1) ++ labels (var x2) + | CatchAll x | CatchAllRef x -> labels (var x) + let const (c : const) = block c.it let global (g : global) = const g.it.ginit diff --git a/interpreter/syntax/operators.ml b/interpreter/syntax/operators.ml index 487c3051..348207df 100644 --- a/interpreter/syntax/operators.ml +++ b/interpreter/syntax/operators.ml @@ -20,19 +20,23 @@ let select t = Select t let block bt es = Block (bt, es) let loop bt es = Loop (bt, es) let if_ bt es1 es2 = If (bt, es1, es2) -let try_catch bt es ct ca = TryCatch (bt, es, ct, ca) -let try_delegate bt es x = TryDelegate (bt, es, x) +let try_table bt cs es = TryTable (bt, cs, es) let br x = Br x let br_if x = BrIf x let br_table xs x = BrTable (xs, x) +let catch x1 x2 = Catch (x1, x2) +let catch_ref x1 x2 = CatchRef (x1, x2) +let catch_all x = CatchAll x +let catch_all_ref x = CatchAllRef x + let return = Return let call x = Call x let call_indirect x y = CallIndirect (x, y) let return_call x = ReturnCall x let return_call_indirect x y = ReturnCallIndirect (x, y) let throw x = Throw x -let rethrow x = Rethrow x +let throw_ref = ThrowRef let local_get x = LocalGet x let local_set x = LocalSet x diff --git a/interpreter/syntax/types.ml b/interpreter/syntax/types.ml index 5b40d8c3..cb153df9 100644 --- a/interpreter/syntax/types.ml +++ b/interpreter/syntax/types.ml @@ -2,7 +2,7 @@ type num_type = I32Type | I64Type | F32Type | F64Type type vec_type = V128Type -type ref_type = FuncRefType | ExternRefType +type ref_type = FuncRefType | ExnRefType | ExternRefType type value_type = NumType of num_type | VecType of vec_type | RefType of ref_type type result_type = value_type list type func_type = FuncType of result_type * result_type @@ -119,10 +119,12 @@ let string_of_vec_type = function let string_of_ref_type = function | FuncRefType -> "funcref" + | ExnRefType -> "exnref" | ExternRefType -> "externref" let string_of_refed_type = function | FuncRefType -> "func" + | ExnRefType -> "exn" | ExternRefType -> "extern" let string_of_value_type = function diff --git a/interpreter/syntax/values.ml b/interpreter/syntax/values.ml index eefe37d5..d4238a11 100644 --- a/interpreter/syntax/values.ml +++ b/interpreter/syntax/values.ml @@ -13,10 +13,12 @@ type num = (I32.t, I64.t, F32.t, F64.t) op type vec = (V128.t) vecop type ref_ = .. -type ref_ += NullRef of ref_type type value = Num of num | Vec of vec | Ref of ref_ +type ref_ += NullRef of ref_type +type ref_ += ExnRef of Tag.t * value list + (* Injection & projection *) @@ -96,7 +98,8 @@ let type_of_num = function let type_of_vec = function | V128 _ -> V128Type -let type_of_ref' = ref (function NullRef t -> t | _ -> assert false) +let type_of_ref' = + ref (function NullRef t -> t | ExnRef _ -> ExnRefType | _ -> assert false) let type_of_ref r = !type_of_ref' r let type_of_value = function @@ -114,6 +117,7 @@ let eq_vec v1 v2 = v1 = v2 let eq_ref' = ref (fun r1 r2 -> match r1, r2 with | NullRef _, NullRef _ -> true + | ExnRef _, ExnRef _ -> r1 == r2 | _, _ -> false ) @@ -169,7 +173,8 @@ let string_of_vec = function let hex_string_of_vec = function | V128 v -> V128.to_hex_string v -let string_of_ref' = ref (function NullRef t -> "null" | _ -> "ref") +let string_of_ref' = + ref (function NullRef t -> "null" | ExnRef _ -> "exn" | _ -> "ref") let string_of_ref r = !string_of_ref' r let string_of_value = function diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index ba3f2bcf..53c463f1 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -456,6 +456,10 @@ let rec instr e = | ReturnCall x -> "return_call " ^ var x, [] | ReturnCallIndirect (x, y) -> "return_call_indirect " ^ var x, [Node ("type " ^ var y, [])] + | Throw x -> "throw " ^ var x, [] + | ThrowRef -> "throw_ref", [] + | TryTable (bt, cs, es) -> + "try_table", block_type bt @ list catch cs @ list instr es | LocalGet x -> "local.get " ^ var x, [] | LocalSet x -> "local.set " ^ var x, [] | LocalTee x -> "local.tee " ^ var x, [] @@ -490,18 +494,6 @@ let rec instr e = | Unary op -> unop op, [] | Binary op -> binop op, [] | Convert op -> cvtop op, [] - | TryCatch (bt, es, ct, ca) -> - let catch (tag, es) = Node ("catch " ^ var tag, list instr es) in - let catch_all = match ca with - | Some es -> [Node ("catch_all", list instr es)] - | None -> [] in - let handler = list catch ct @ catch_all in - "try", block_type bt @ [Node ("do", list instr es)] @ handler - | TryDelegate (bt, es, x) -> - let delegate = [Node ("delegate " ^ var x, [])] in - "try", block_type bt @ [Node ("do", list instr es)] @ delegate - | Throw x -> "throw " ^ var x, [] - | Rethrow x -> "rethrow " ^ var x, [] | VecConst v -> vec_constop v.it ^ " " ^ vec v, [] | VecTest op -> vec_testop op, [] | VecUnary op -> vec_unop op, [] @@ -519,6 +511,13 @@ let rec instr e = | VecReplace op -> vec_replaceop op, [] in Node (head, inner) +and catch c = + match c.it with + | Catch (x1, x2) -> Node ("catch " ^ var x1 ^ " " ^ var x2, []) + | CatchRef (x1, x2) -> Node ("catch_ref " ^ var x1 ^ " " ^ var x2, []) + | CatchAll x -> Node ("catch_all " ^ var x, []) + | CatchAllRef x -> Node ("catch_all_ref " ^ var x, []) + let const head c = match c.it with | [e] -> instr e diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index 9a7a5482..f7d09144 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -147,7 +147,9 @@ rule token = parse | "f32x4" -> VEC_SHAPE (V128.F32x4 ()) | "f64x2" -> VEC_SHAPE (V128.F64x2 ()) + | "exn" -> EXN | "extern" -> EXTERN + | "exnref" -> EXNREF | "externref" -> EXTERNREF | "funcref" -> FUNCREF | "mut" -> MUT @@ -170,6 +172,13 @@ rule token = parse | "call_indirect" -> CALL_INDIRECT | "return_call" -> RETURN_CALL | "return_call_indirect" -> RETURN_CALL_INDIRECT + | "try_table" -> TRY_TABLE + | "catch" -> CATCH + | "catch_ref" -> CATCH_REF + | "catch_all" -> CATCH_ALL + | "catch_all_ref" -> CATCH_ALL_REF + | "throw" -> THROW + | "throw_ref" -> THROW_REF | "local.get" -> LOCAL_GET | "local.set" -> LOCAL_SET @@ -276,6 +285,7 @@ rule token = parse | "ref.null" -> REF_NULL | "ref.func" -> REF_FUNC + | "ref.exn" -> REF_EXN | "ref.extern" -> REF_EXTERN | "ref.is_null" -> REF_IS_NULL @@ -655,14 +665,6 @@ rule token = parse | "f32x4.replace_lane" -> VEC_REPLACE f32x4_replace_lane | "f64x2.replace_lane" -> VEC_REPLACE f64x2_replace_lane - | "try" -> TRY - | "do" -> DO - | "catch" -> CATCH - | "catch_all" -> CATCH_ALL - | "delegate" -> DELEGATE - | "throw" -> THROW - | "rethrow" -> RETHROW - | "type" -> TYPE | "func" -> FUNC | "param" -> PARAM diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 429c31c8..be41cd7a 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -144,9 +144,6 @@ let func_type (c : context) x = try (Lib.List32.nth c.types.list x.it).it with Failure _ -> error x.at ("unknown type " ^ Int32.to_string x.it) -let handlers (c : context) h = - List.map (fun (l, i) -> (l c tag, i c)) h - let anon category space n = let i = space.count in space.count <- Int32.add i n; @@ -214,10 +211,10 @@ let inline_type_explicit (c : context) x ft at = %token NUM_TYPE %token VEC_TYPE %token VEC_SHAPE -%token FUNCREF EXTERNREF EXTERN MUT +%token FUNCREF EXNREF EXTERNREF EXN EXTERN MUT %token UNREACHABLE NOP DROP SELECT -%token BLOCK END IF THEN ELSE LOOP BR BR_IF BR_TABLE TRY DO CATCH CATCH_ALL -%token DELEGATE +%token BLOCK END IF THEN ELSE LOOP BR BR_IF BR_TABLE +%token TRY_TABLE CATCH CATCH_REF CATCH_ALL CATCH_ALL_REF THROW THROW_REF %token CALL CALL_INDIRECT RETURN RETURN_CALL RETURN_CALL_INDIRECT %token LOCAL_GET LOCAL_SET LOCAL_TEE GLOBAL_GET GLOBAL_SET %token TABLE_GET TABLE_SET @@ -227,8 +224,7 @@ let inline_type_explicit (c : context) x ft at = %token OFFSET_EQ_NAT ALIGN_EQ_NAT %token Ast.instr' * Values.num> CONST %token UNARY BINARY TEST COMPARE CONVERT -%token REF_NULL REF_FUNC REF_EXTERN REF_IS_NULL -%token THROW RETHROW +%token REF_NULL REF_FUNC REF_EXN REF_EXTERN REF_IS_NULL %token Memory.offset -> Ast.instr'> VEC_LOAD VEC_STORE %token Memory.offset -> int -> Ast.instr'> VEC_LOAD_LANE VEC_STORE_LANE %token string Source.phrase list -> Source.region -> Ast.instr' * Values.vec> VEC_CONST @@ -267,10 +263,12 @@ string_list : ref_kind : | FUNC { FuncRefType } + | EXN { ExnRefType } | EXTERN { ExternRefType } ref_type : | FUNCREF { FuncRefType } + | EXNREF { ExnRefType } | EXTERNREF { ExternRefType } value_type : @@ -397,7 +395,7 @@ plain_instr : | CALL var { fun c -> call ($2 c func) } | RETURN_CALL var { fun c -> return_call ($2 c func) } | THROW var { fun c -> throw ($2 c tag) } - | RETHROW var { fun c -> rethrow ($2 c label) } + | THROW_REF { fun c -> throw_ref } | LOCAL_GET var { fun c -> local_get ($2 c local) } | LOCAL_SET var { fun c -> local_set ($2 c local) } | LOCAL_TEE var { fun c -> local_tee ($2 c local) } @@ -522,12 +520,9 @@ block_instr : | IF labeling_opt block ELSE labeling_end_opt instr_list END labeling_end_opt { fun c -> let c' = $2 c ($5 @ $8) in let ts, es1 = $3 c' in if_ ts es1 ($6 c') } - | TRY labeling_opt block handler_instr - { fun c -> let c' = $2 c [] in - let ts, es = $3 c' in $4 ts es c' } - | TRY labeling_opt block DELEGATE var - { fun c -> let c' = $2 c [] in - let ts, es = $3 c' in try_delegate ts es ($5 c label) } + | TRY_TABLE labeling_opt handler_block END labeling_end_opt + { fun c -> let c' = $2 c $5 in + let bt, (cs, es) = $3 c c' in try_table bt cs es } block : | type_use block_param_body @@ -557,43 +552,49 @@ block_result_body : { let FuncType (ins, out) = fst $5 in FuncType (ins, $3 @ out), snd $5 } -handler_instr : - | catch_list_instr END - { fun bt es c -> try_catch bt es (handlers c $1) None } - | catch_list_instr catch_all END - { fun bt es c -> try_catch bt es (handlers c $1) (Some ($2 c)) } - | catch_all END - { fun bt es c -> try_catch bt es [] (Some ($1 c)) } - | END { fun bt es c -> try_catch bt es [] None } - -catch_list_instr : - | catch catch_list_instr { $1 :: $2 } - | catch { [$1] } - -handler : - | catch_list - { fun bt es _ c' -> - let cs = (List.map (fun (l, i) -> (l c' tag, i c')) $1) in - try_catch bt es cs None } - | catch_list LPAR catch_all RPAR - { fun bt es _ c' -> - let cs = (List.map (fun (l, i) -> (l c' tag, i c')) $1) in - try_catch bt es cs (Some ($3 c')) } - | LPAR catch_all RPAR - { fun bt es _ c' -> try_catch bt es [] (Some ($2 c')) } - | LPAR DELEGATE var RPAR - { fun bt es c _ -> try_delegate bt es ($3 c label) } - | /* empty */ { fun bt es c _ -> try_catch bt es [] None } - -catch_list : - | catch_list LPAR catch RPAR { $1 @ [$3] } - | LPAR catch RPAR { [$2] } - -catch : - | CATCH var instr_list { ($2, $3) } - -catch_all : - | CATCH_ALL instr_list { $2 } + +handler_block : + | type_use handler_block_param_body + { let at1 = ati 1 in + fun c c' -> let ft, esh = $2 c c' in + VarBlockType (inline_type_explicit c ($1 c type_) ft at1), esh } + | handler_block_param_body /* Sugar */ + { let at = at () in + fun c c' -> let ft, esh = $1 c c' in + let bt = + match ft with + | FuncType ([], []) -> ValBlockType None + | FuncType ([], [t]) -> ValBlockType (Some t) + | ft -> VarBlockType (inline_type c ft at) + in bt, esh } + +handler_block_param_body : + | handler_block_result_body { $1 } + | LPAR PARAM value_type_list RPAR handler_block_param_body + { fun c c' -> let FuncType (ins, out), esh = $5 c c' in + FuncType ($3 @ ins, out), esh } + +handler_block_result_body : + | handler_block_body { fun c c' -> FuncType ([], []), $1 c c' } + | LPAR RESULT value_type_list RPAR handler_block_result_body + { fun c c' -> let FuncType (ins, out), esh = $5 c c' in + FuncType (ins, $3 @ out), esh } + +handler_block_body : + | instr_list + { fun c c' -> [], $1 c' } + | LPAR CATCH var var RPAR handler_block_body + { fun c c' -> let cs, es = $6 c c' in + (catch ($3 c tag) ($4 c label) @@ ati 2) :: cs, es } + | LPAR CATCH_REF var var RPAR handler_block_body + { fun c c' -> let cs, es = $6 c c' in + (catch_ref ($3 c tag) ($4 c label) @@ ati 2) :: cs, es } + | LPAR CATCH_ALL var RPAR handler_block_body + { fun c c' -> let cs, es = $5 c c' in + (catch_all ($3 c label) @@ ati 2) :: cs, es } + | LPAR CATCH_ALL_REF var RPAR handler_block_body + { fun c c' -> let cs, es = $5 c c' in + (catch_all_ref ($3 c label) @@ ati 2) :: cs, es } expr : /* Sugar */ @@ -621,8 +622,9 @@ expr1 : /* Sugar */ | IF labeling_opt if_block { fun c -> let c' = $2 c [] in let bt, (es, es1, es2) = $3 c c' in es, if_ bt es1 es2 } - | TRY labeling_opt try_block - { fun c -> let c' = $2 c [] in [], $3 c c' } + | TRY_TABLE labeling_opt try_block + { fun c -> let c' = $2 c [] in + let bt, (cs, es) = $3 c c' in [], try_table bt cs es } select_expr_results : | LPAR RESULT value_type_list RPAR select_expr_results @@ -692,37 +694,53 @@ if_ : | LPAR THEN instr_list RPAR /* Sugar */ { fun c c' -> [], $3 c', [] } + try_block : | type_use try_block_param_body { let at = at () in fun c c' -> - let bt = VarBlockType (inline_type_explicit c' ($1 c' type_) (fst $2) at) in - snd $2 bt c c' } + let ft, esh = $2 c c' in + let bt = VarBlockType (inline_type_explicit c' ($1 c' type_) ft at) in + bt, esh } | try_block_param_body /* Sugar */ { let at = at () in fun c c' -> + let ft, esh = $1 c c' in let bt = - match fst $1 with + match ft with | FuncType ([], []) -> ValBlockType None | FuncType ([], [t]) -> ValBlockType (Some t) - | ft -> VarBlockType (inline_type c' ft at) - in snd $1 bt c c' } + | _ -> VarBlockType (inline_type c' ft at) + in bt, esh } try_block_param_body : | try_block_result_body { $1 } | LPAR PARAM value_type_list RPAR try_block_param_body - { let FuncType (ins, out) = fst $5 in - FuncType ($3 @ ins, out), snd $5 } + { fun c c' -> let FuncType (ins, out), esh = $5 c c' in + FuncType ($3 @ ins, out), esh } try_block_result_body : - | try_ { FuncType ([], []), $1 } + | try_block_handler_body { fun c c' -> FuncType ([], []), $1 c c' } | LPAR RESULT value_type_list RPAR try_block_result_body - { let FuncType (ins, out) = fst $5 in - FuncType (ins, $3 @ out), snd $5 } + { fun c c' -> let FuncType (ins, out), esh = $5 c c' in + FuncType (ins, $3 @ out), esh } + +try_block_handler_body : + | instr_list + { fun c c' -> [], $1 c' } + | LPAR CATCH var var RPAR try_block_handler_body + { fun c c' -> let cs, es = $6 c c' in + (catch ($3 c tag) ($4 c label) @@ ati 2) :: cs, es } + | LPAR CATCH_REF var var RPAR try_block_handler_body + { fun c c' -> let cs, es = $6 c c' in + (catch_ref ($3 c tag) ($4 c label) @@ ati 2) :: cs, es } + | LPAR CATCH_ALL var RPAR try_block_handler_body + { fun c c' -> let cs, es = $5 c c' in + (catch_all ($3 c label) @@ ati 2) :: cs, es } + | LPAR CATCH_ALL_REF var RPAR try_block_handler_body + { fun c c' -> let cs, es = $5 c c' in + (catch_all_ref ($3 c label) @@ ati 2) :: cs, es } -try_ : - | LPAR DO instr_list RPAR handler - { fun bt c c' -> $5 bt ($3 c') c c' } expr_list : | /* empty */ { fun c -> [] } @@ -1242,6 +1260,7 @@ result : | LPAR CONST NAN RPAR { NumResult (NanPat (nanop $2 ($3 @@ ati 3))) @@ at () } | literal_ref { RefResult (RefPat ($1 @@ at ())) @@ at () } | LPAR REF_FUNC RPAR { RefResult (RefTypePat FuncRefType) @@ at () } +/*| LPAR REF_EXN RPAR { RefResult (RefTypePat ExnRefType) @@ at () }*/ | LPAR REF_EXTERN RPAR { RefResult (RefTypePat ExternRefType) @@ at () } | LPAR VEC_CONST VEC_SHAPE numpat_list RPAR { if V128.num_lanes $3 <> List.length $4 then diff --git a/interpreter/valid/valid.ml b/interpreter/valid/valid.ml index f6d8a921..1f177519 100644 --- a/interpreter/valid/valid.ml +++ b/interpreter/valid/valid.ml @@ -14,8 +14,6 @@ let require b at s = if not b then error at s (* Context *) -type label_kind = BlockLabel | CatchLabel - type context = { types : func_type list; @@ -28,7 +26,7 @@ type context = datas : unit list; locals : value_type list; results : value_type list; - labels : (label_kind * result_type) list; + labels : result_type list; refs : Free.t; } @@ -257,34 +255,34 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : op_type | Block (bt, es) -> let FuncType (ts1, ts2) as ft = check_block_type c bt in - check_block {c with labels = (BlockLabel, ts2) :: c.labels} es ft e.at; + check_block {c with labels = ts2 :: c.labels} es ft e.at; ts1 --> ts2 | Loop (bt, es) -> let FuncType (ts1, ts2) as ft = check_block_type c bt in - check_block {c with labels = (BlockLabel, ts1) :: c.labels} es ft e.at; + check_block {c with labels = ts1 :: c.labels} es ft e.at; ts1 --> ts2 | If (bt, es1, es2) -> let FuncType (ts1, ts2) as ft = check_block_type c bt in - check_block {c with labels = (BlockLabel, ts2) :: c.labels} es1 ft e.at; - check_block {c with labels = (BlockLabel, ts2) :: c.labels} es2 ft e.at; + check_block {c with labels = ts2 :: c.labels} es1 ft e.at; + check_block {c with labels = ts2 :: c.labels} es2 ft e.at; (ts1 @ [NumType I32Type]) --> ts2 | Br x -> - let (_, ts) = label c x in + let ts = label c x in ts -->... [] | BrIf x -> - let (_, ts) = label c x in + let ts = label c x in (ts @ [NumType I32Type]) --> ts | BrTable (xs, x) -> - let (_, ts) = label c x in + let ts = label c x in let n = List.length ts in let ts' = Lib.List.table n (fun i -> peek (n - i) s) in check_stack ts' (known ts) x.at; - List.iter (fun x' -> check_stack ts' (known (snd (label c x'))) x'.at) xs; + List.iter (fun x' -> check_stack ts' (known (label c x')) x'.at) xs; (ts' @ [Some (NumType I32Type)]) -~>... [] | Return -> @@ -318,24 +316,14 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : op_type let FuncType (ts1, _) = type_ c (y @@ e.at) in ts1 -->... [] - | Rethrow x -> - let (kind, _) = label c x in - require (kind = CatchLabel) e.at "invalid rethrow label"; - [] -->... [] - - | TryCatch (bt, es, cts, ca) -> - let FuncType (ts1, ts2) as ft = check_block_type c bt in - let c_try = {c with labels = (BlockLabel, ts2) :: c.labels} in - let c_catch = {c with labels = (CatchLabel, ts2) :: c.labels} in - check_block c_try es ft e.at; - List.iter (fun ct -> check_catch ct c_catch ft e.at) cts; - Lib.Option.app (fun es -> check_block c_catch es ft e.at) ca; - ts1 --> ts2 + | ThrowRef -> + [RefType ExnRefType] -->... [] - | TryDelegate (bt, es, x) -> + | TryTable (bt, cs, es) -> let FuncType (ts1, ts2) as ft = check_block_type c bt in - ignore (label c x); - check_block {c with labels = (BlockLabel, ts2) :: c.labels} es ft e.at; + let c' = {c with labels = ts2 :: c.labels} in + List.iter (fun ct -> check_catch c ct ts2 e.at) cs; + check_block c' es ft e.at; ts1 --> ts2 | LocalGet x -> @@ -577,12 +565,30 @@ and check_block (c : context) (es : instr list) (ft : func_type) at = ("type mismatch: block requires " ^ string_of_result_type ts2 ^ " but stack has " ^ string_of_infer_types (snd s)) -and check_catch (ct : var * instr list) (c : context) (ft : func_type) at = - let (x, es) = ct in - let TagType y = tag c x in - let FuncType (ts1, _) = type_ c (y @@ at) in - let FuncType (_, ts2) = ft in - check_block c es (FuncType (ts1, ts2)) at +and check_catch (c : context) (cc : catch) (ts : value_type list) at = + match cc.it with + | Catch (x1, x2) -> + let TagType y = tag c x1 in + let FuncType (ts1, _) = type_ c (y @@ at) in + require (label c x2 = ts1) at + ("type mismatch: catch handler requires " ^ string_of_result_type ts1 ^ + " but label has " ^ string_of_result_type (label c x2)) + | CatchRef (x1, x2) -> + let TagType y = tag c x1 in + let FuncType (ts1, _) = type_ c (y @@ at) in + require (label c x2 = ts1 @ [RefType ExnRefType]) at + ("type mismatch: catch handler requires " ^ + string_of_result_type (ts1 @ [RefType ExnRefType]) ^ + " but label has " ^ string_of_result_type (label c x2)) + | CatchAll x -> + require (label c x = []) at + ("type mismatch: catch handler requires " ^ string_of_result_type [] ^ + " but label has " ^ string_of_result_type (label c x)) + | CatchAllRef x -> + require (label c x = [RefType ExnRefType]) at + ("type mismatch: catch handler requires " ^ + string_of_result_type [RefType ExnRefType] ^ + " but label has " ^ string_of_result_type (label c x)) (* Types *) @@ -652,7 +658,7 @@ let check_type (t : type_) = let check_func (c : context) (f : func) = let {ftype; locals; body} = f.it in let FuncType (ts1, ts2) = type_ c ftype in - let c' = {c with locals = ts1 @ locals; results = ts2; labels = [(BlockLabel, ts2)]} in + let c' = {c with locals = ts1 @ locals; results = ts2; labels = [ts2]} in check_block c' body (FuncType ([], ts2)) f.at let check_tag (c : context) (t : tag) = diff --git a/proposals/exception-handling/Exceptions.md b/proposals/exception-handling/Exceptions.md index afaa78f3..76661c3f 100644 --- a/proposals/exception-handling/Exceptions.md +++ b/proposals/exception-handling/Exceptions.md @@ -36,7 +36,7 @@ succeeding instructions to process the data. A WebAssembly exception is created when you throw it with the `throw` instruction. Thrown exceptions are handled as follows: -1. They can be caught by one of `catch`/`catch_all` blocks in an enclosing try +1. They can be caught by one of the *catch clauses* in an enclosing try block of a function body. 1. Throws not caught within a function body continue up the call stack, popping @@ -86,62 +86,50 @@ Exception tag indices are used by: 1. The `throw` instruction which creates a WebAssembly exception with the corresponding exception tag, and then throws it. -2. The `catch` clause uses the tag to identify if the thrown exception is one it - can catch. If true it pushes the corresponding argument values of the +2. Catch clauses use a tag to identify the thrown exception it + can catch. If it matches, it pushes the corresponding argument values of the exception onto the stack. -### Try-catch blocks +### Exception references + +When caught, an exception is reified into an _exception reference_, a value of the new type `exnref`. +Exception references can be used to rethrow the caught exception. + +### Try blocks A _try block_ defines a list of instructions that may need to process exceptions and/or clean up state when an exception is thrown. Like other higher-level -constructs, a try block begins with a `try` instruction, and ends with an `end` -instruction. That is, a try-catch block is sequence of instructions having the +constructs, a try block begins with a `try_table` instruction, and ends with an `end` +instruction. That is, a try block is sequence of instructions having the following form: ``` -try blocktype - instruction* -catch i - instruction* -catch j - instruction* -... -catch_all +try_table blocktype catch* instruction* end ``` -A try-catch block contains zero or more `catch` blocks and zero or one -`catch_all` block. All `catch` blocks must precede the `catch_all` block, if -any. The `catch`/`catch_all` instructions (within the try construct) are called -the _catching_ instructions. There may not be any `catch` or `catch_all` blocks -after a `try`, in which case the `try` block does not catch any exceptions. - -The _body_ of the try block is the list of instructions before the first -catching instruction. The _body_ of each catch block is the sequence of -instructions following the corresponding catching instruction before the next -catching instruction (or the `end` instruction if it is the last catching -block). - -The `catch` instruction has an exception tag associated with it. The tag -identifies what exceptions it can catch. That is, any exception created with the -corresponding exception tag. Catch blocks that begin with a `catch` instruction -are considered _tagged_ catch blocks. - -The last catching instruction of a try-catch block can be the `catch_all` -instruction. If it begins with the `catch_all` instruction, it defines the -_default_ catch block. The default catch block has no tag index, and is used to -catch all exceptions not caught by any of the tagged catch blocks. The term -'catch block' refers to both `catch` and `catch_all` blocks. - -When the program runs `br` within `catch` or `catch_all` blocks, the rest of -the catching blocks will not run and the program control will branch to the -destination, as in normal blocks. +A try block contains zero or more _catch clauses_. If there are no catch clauses, then the try block does not catch any exceptions. + +The _body_ of the try block is the list of instructions after the last +catch clause, if any. + +Each `catch` clause can be in one of 4 forms: +``` +catch tag label +catch_ref tag label +catch_all label +catch_all_ref label +``` +All forms have a label which is branched to when an exception is cought (see below). +The former two forms have an exception tag associated with it that +identifies what exceptions it will catch. +The latter two forms catch any exception, so that they can be used to define a _default_ handler. Try blocks, like control-flow blocks, have a _block type_. The block type of a try block defines the values yielded by evaluating the try block when either no -exception is thrown, or the exception is successfully caught by the catch block. -Because `try` and `end` instructions define a control-flow block, they can be +exception is thrown, or the exception is successfully caught by the catch clause. +Because `try_table` defines a control-flow block, it can be targets for branches (`br` and `br_if`) as well. ### Throwing an exception @@ -168,280 +156,40 @@ an an enclosing try block, or the call stack is flushed. If the call stack is flushed, the embedder defines how to handle uncaught exceptions. Otherwise, the found enclosing try block is the catching try block. -A throw inside the body of a catch block is never caught by the corresponding -try block of the catch block, since instructions in the body of the catch block -are not in the body of the try block. - Once a catching try block is found for the thrown exception, the operand stack is popped back to the size the operand stack had when the try block was entered after possible block parameters were popped. -Then in case of a try-catch block, tagged catch blocks are tried in the order -they appear in the catching try block, until one matches. If a matched tagged -catch block is found, control is transferred to the body of the catch block, and +Then catch clauses are tried in the order +they appear in the catching try block, until one matches. If a matching catch clause is found, control is transferred to the label of that catch clause. +In case of `catch` or `catch_ref`, the arguments of the exception are pushed back onto the stack. +For `catch_ref` and `catch_all_ref`, an exception reference is then pushed to the stack, which represents the caught exception. -Otherwise, control is transferred to the body of the `catch_all` block, if any. -However, unlike tagged catch blocks, the constructor arguments are not copied -back onto the operand stack. +If no catch clauses were matched, the exception is implicitly rethrown. -If no tagged catch blocks were matched, and the catching try block doesn't have -a `catch_all` block, the exception is rethrown. - -If control is transferred to the body of a catch block, and the last instruction -in the body is executed, control then exits the try block. - -If the selected catch block does not throw an exception, it must yield the -value(s) specified by the type annotation on the corresponding catching try -block. - -Note that a caught exception can be rethrown using the `rethrow` instruction. +Note that a caught exception can be rethrown explicitly using the `exnref` and the `throw_ref` instruction. ### Rethrowing an exception -The `rethrow` instruction can only appear in the body of a catch/catch_all -block. It always re-throws the exception caught by an enclosing catch block. - -Associated with the `rethrow` instruction is a _label_. The label is used to -disambiguate which exception is to be rethrown, when inside nested catch blocks. -The label is the relative block depth to the corresponding try block for which -the catching block appears. - -For example consider the following: - -``` -try $l1 - ... -catch ;; $l1 - ... - block - ... - try $l2 - ... - catch ;; $l2 - ... - try $l3 - ... - catch ;; $l3 - ... - rethrow N ;; (or label) - end - end - end - ... -end -``` - -In this example, `N` is used to disambiguate which caught exception is being -rethrown. It could rethrow any of the three caught expceptions. Hence, `rethrow -0` corresponds to the exception caught by `catch 3`, `rethrow 1` corresponds to -the exception caught by `catch 2`, and `rethrow 3` corresponds to the exception -caught by `catch 1`. In wat format, the argument for the `rethrow` instructions -can also be written as a label, like branches. So `rethrow 0` in the example -above can also be written as `rethrow $l3`. - -Note that `rethrow 2` is not allowed because it does not refer to a `try` label -from within its catch block. Rather, it references a `block` instruction, so it -will result in a validation failure. - -Note that the example below is a validation failure: -``` -try $l1 - try $l2 - rethrow $l2 ;; (= rethrow 0) - catch - end -catch -end -``` -The `rethrow` here references `try $l2`, but the `rethrow` is not within its -`catch` block. - -The example below includes all of the cases explained above. The numbers -within `()` after `rethrow`s are the label operands in immediate values. -``` -(func $test - try $lA - ... - catch ($lA) - ... - block $lB - ... - try $lC - ... - catch ($lC) - ... - try $lD - ... - rethrow $lD (0) ;; refers to 'catch ($lD)', but it is not within 'catch ($lD)', so validation failure - rethrow $lC (1) ;; rethrows the exception caught by catch ($lC) - rethrow $lB (2) ;; refers to 'block $lB', so validation failure - rethrow $lA (3) ;; rethrows the exception caught by catch ($lA) - rethrow 4 ;; validation failure - catch ($lD) - ... - rethrow $lD (0) ;; rethrows the exception caught by catch ($lD) - rethrow $lC (1) ;; rethrows the exception caught by catch ($lC) - rethrow $lB (2) ;; refers to 'block $lB', so validation failure - rethrow $lA (3) ;; rethrows the exception caught by catch ($lA) - rethrow 4 ;; validation failure - end - end - end - ... - end -``` - -### Try-delegate blocks - -Try blocks can also be used with the `delegate` instruction. A try-delegate -block contains a `delegate` instruction with the following form: - -``` -try blocktype - instruction* -delegate label -``` - -The `delegate` clause does not have an associated body, so try-delegate blocks -don't have an `end` instruction at the end. The `delegate` instruction takes a -try label and delegates exception handling to a `catch`/`catch_all`/`delegate` -specified by the `try` label. For example, consider this code: - -``` -try $l0 - try - call $foo - delegate $l0 ;; (= delegate 0) -catch - ... -catch_all - ... -end -``` - -If `call $foo` throws, searching for a catching block first finds `delegate`, -and because it delegates exception handling to catching instructions associated -with `$l1`, it will be next checked by the outer `catch` and then `catch_all` -instructions. - -`delegate` can also target `catch`-less `try`s or non-`try` block constructs -like `block`s or `loop`s, in which case the delegated exception is assumed to -propagate to the outer scope and will be caught by the next matching -try-catches, or rethrown to the caller if there is no outer try block. In the -examples, catches are annotated with `($label_name)` to clarify which `try` it -belongs to for clarification; it is not the official syntax. -``` -try $l0 - block $l1 - try - call $foo - delegate $l1 ;; delegates to 'catch ($l0)' - end -catch ($l0) -end -``` - -Like branches, `delegate` can only target outer blocks, and effectively -rethrows the exception in that block. Consequently, delegating to a specific -`catch` or `catch_all` handler requires targeting the respective label from -within the associated `try` block. Delegating to a label from within a `catch` -block does delegate the exception to the next enclosing handler -- analogous to -performing a `throw` within a `catch` block, that handler is no longer active -at that point. Here is another example: - -``` -try $l0 - try $l1 - catch ($l1) - try - call $foo - delegate $l1 ;; delegates to 'catch ($l0)' - catch_all - ... - end -catch ($l0) -``` - -Here the `delegate` is targeting `catch ($l1)`, which exists before the -`delegate`. So in case an exception occurs, it propagates out and ends up -targetting `catch ($l0)`, if the catch has a matching tag. If not, it will -propagate further out. Even if the `catch_all` is below the `delegate`, -`delegate` targets catches of a `try` as a whole and does not target an -individual `catch`/`catch_all`, so it doesn't apply. - -If `delegate` targets the implicit function body block, then in effect it -delegates the exception to the caller of the current function. For example: -``` -(func $test - try - try - call $foo - delegate 1 ;; delegates to the caller - catch - ... - catch_all - ... - end -) -``` -In case `foo` throws, `delegate 1` here delegates the exception handling to the -caller, i.e., the exception escapes the current function. If the immediate is -greater than or equal to the number of block nesting including the implicit -function-level block, it is a validation failure. In this example, any number -equal to or greater than 2 is not allowed. - -The below is an example that includes all the cases explained. The numbers -within `()` after `delegate`s are the label operands in immediate values. -``` -(func $test - try $lA - block $lB - try $lC - try - delegate $lC (0) ;; delegates to 'catch ($lC)' - try - delegate $lB (1) ;; $lB is a block, so delegates to 'catch ($lA)' - try - delegate $lA (2) ;; delegates to 'catch ($lA)' - try - delegate 3 ;; propagates to the caller - try - delegate 4 ;; validation failure - catch ($lC) - try - delegate $lC (0) ;; 'catch ($lC)' is above this instruction, - ;; so delegates to 'catch ($lA)' - try - delegate $lB (1) ;; $lB is a block, so delegates to 'catch ($lA)' - try - delegate $lA (2) ;; delegates to 'catch ($lA)' - try - delegate 3 ;; propagates to the caller - try - delegate 4 ;; validation failure - end ;; try $lC - end ;; block $lB - catch ($lA) - end ;; try $lA -) -``` +The `throw_ref` takes an operand of type `exnref` and re-throws the corresponding caught exception. +If the operand is null, a trap occurs. ### JS API #### Traps -The `catch`/`catch_all` instruction catches exceptions generated by the `throw` -instruction, but does not catch traps. The rationale for this is that in general +Catch clauses handle exceptions generated by the `throw` +instruction, but do not catch traps. The rationale for this is that in general traps are not locally recoverable and are not needed to be handled in local -scopes like try-catch. +scopes like try blocks. -The `catch` instruction catches foreign exceptions generated from calls to +The `try_table` instruction catches foreign exceptions generated from calls to function imports as well, including JavaScript exceptions, with a few exceptions: 1. In order to be consistent before and after a trap reaches a JavaScript frame, - the `catch` instruction does not catch exceptions generated from traps. -1. The `catch` instruction does not catch JavaScript exceptions generated from + the `try_table` instruction does not catch exceptions generated from traps. +1. The `try_table` instruction does not catch JavaScript exceptions generated from stack overflow and out of memory. Filtering these exceptions should be based on a predicate that is not observable @@ -498,8 +246,8 @@ When `ExceptionOption` is not provided or it does not contain `traceStack` entry, `traceStack` is considered `false` by default. To preserve stack trace info when crossing the JS to Wasm boundary, exceptions -can internally contain a stack trace, which is propagated when caught by `catch` -and rethrown by `rethrow`. +can internally contain a stack trace, which is propagated when caught by a `catch[_all]_ref` clause +and rethrown by `throw_ref`. More formally, the added interfaces look like the following: @@ -542,17 +290,16 @@ document](https://github.com/WebAssembly/spec/blob/master/document/core/text/ins The following rules are added to *instructions*: ``` - try blocktype instruction* (catch tag_index instruction*)* (catch_all instruction*)? end | - try blocktype instruction* delegate label | - throw tag_index argument* | - rethrow label | + try_table blocktype catch* instruction* end | + throw tag_index | + throw_ref label | ``` -Like the `block`, `loop`, and `if` instructions, the `try` instruction is +Like the `block`, `loop`, and `if` instructions, the `try_table` instruction is *structured* control flow instruction, and can be labeled. This allows branch instructions to exit try blocks. -The `tag_index` of the `throw` and `catch` instructions denotes the exception +The `tag_index` of the `throw` and `catch[_ref]` clauses denotes the exception tag to use when creating/extract from an exception. See [tag index space](#tag-index-space) for further clarification of exception tags. @@ -580,6 +327,10 @@ document](https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md). #### Other Types +##### exnref + +The type `exnref` is represented by the type opcode `-0x17`. + ##### tag_type We reserve a bit to denote the exception attribute: @@ -679,19 +430,25 @@ follows: The tag names subsection is a `name_map` which assigns names to a subset of the tag indices (Used for both imports and module-defined). -### Control flow operators +### Control flow instructions -The control flow operators are extended to define try blocks, catch blocks, -throws, and rethrows as follows: +The control flow instructions are extended to define try blocks and +throws as follows: | Name | Opcode | Immediates | Description | | ---- | ---- | ---- | ---- | -| `try` | `0x06` | sig : `blocktype` | begins a block which can handle thrown exceptions | -| `catch` | `0x07` | index : `varint32` | begins the catch block of the try block | -| `catch_all` | `0x19` | | begins the catch_all block of the try block | -| `delegate` | `0x18` | relative_depth : `varuint32` | begins the delegate block of the try block | -| `throw` | `0x08` | index : `varint32` | Creates an exception defined by the tag and then throws it | -| `rethrow` | `0x09` | relative_depth : `varuint32` | Pops the `exnref` on top of the stack and throws it | - -The *sig* fields of `block`, `if`, and `try` operators are block signatures +| `try_table` | `0x1f` | sig : `blocktype`, n : `varuint32`, catch : `catch^n` | begins a block which can handle thrown exceptions | +| `throw` | `0x08` | index : `varuint32` | Creates an exception defined by the tag and then throws it | +| `throw_ref` | `0x0a` | | Pops an `exnref` from the stack and throws it | + +The *sig* fields of `block`, `if`, and `try_table` instructions are block types which describe their use of the operand stack. + +A `catch` handler is a pair of tag and label index: + +| Name | Opcode | Immediates | +| ------- | ------ | ----------- | +| `catch` | `0x00` | tag : `varuint32`, label : `varuint32` | +| `catch_ref` | `0x01` | tag : `varuint32`, label : `varuint32` | +| `catch_all` | `0x02` | label : `varuint32` | +| `catch_all_ref` | `0x02` | label : `varuint32` | diff --git a/test/core/binary.wast b/test/core/binary.wast index df176126..7e437317 100644 --- a/test/core/binary.wast +++ b/test/core/binary.wast @@ -118,7 +118,7 @@ ;; Missing end marker here "\0a\04\01\02\00\0b" ;; Code section: 1 function ) - "illegal opcode" + "unexpected end of section or function" ) ;; memory.grow reserved byte equal to zero. diff --git a/test/core/ref_null.wast b/test/core/ref_null.wast index b88b0888..eb4a3163 100644 --- a/test/core/ref_null.wast +++ b/test/core/ref_null.wast @@ -1,10 +1,13 @@ (module (func (export "externref") (result externref) (ref.null extern)) + (func (export "exnref") (result exnref) (ref.null exn)) (func (export "funcref") (result funcref) (ref.null func)) (global externref (ref.null extern)) + (global exnref (ref.null exn)) (global funcref (ref.null func)) ) (assert_return (invoke "externref") (ref.null extern)) +(assert_return (invoke "exnref") (ref.null exn)) (assert_return (invoke "funcref") (ref.null func)) diff --git a/test/core/throw.wast b/test/core/throw.wast index 2148d5ea..dc1aa4a9 100644 --- a/test/core/throw.wast +++ b/test/core/throw.wast @@ -26,15 +26,12 @@ (func $throw-1-2 (i32.const 1) (i32.const 2) (throw $e-i32-i32)) (func (export "test-throw-1-2") - (try - (do (call $throw-1-2)) - (catch $e-i32-i32 - (i32.const 2) - (if (i32.ne) (then (unreachable))) - (i32.const 1) - (if (i32.ne) (then (unreachable))) - ) + (block $h (result i32 i32) + (try_table (catch $e-i32-i32 $h) (call $throw-1-2)) + (return) ) + (if (i32.ne (i32.const 2)) (then (unreachable))) + (if (i32.ne (i32.const 1)) (then (unreachable))) ) ) diff --git a/test/core/throw_ref.wast b/test/core/throw_ref.wast new file mode 100644 index 00000000..f59710a1 --- /dev/null +++ b/test/core/throw_ref.wast @@ -0,0 +1,118 @@ +;; Test throw_ref instruction. + +(module + (tag $e0) + (tag $e1) + + (func (export "catch-throw_ref-0") + (block $h (result exnref) + (try_table (catch_ref $e0 $h) (throw $e0)) + (unreachable) + ) + (throw_ref) + ) + + (func (export "catch-throw_ref-1") (param i32) (result i32) + (block $h (result exnref) + (try_table (result i32) (catch_ref $e0 $h) (throw $e0)) + (return) + ) + (if (param exnref) (i32.eqz (local.get 0)) + (then (throw_ref)) + (else (drop)) + ) + (i32.const 23) + ) + + (func (export "catchall-throw_ref-0") + (block $h (result exnref) + (try_table (result exnref) (catch_all_ref $h) (throw $e0)) + ) + (throw_ref) + ) + + (func (export "catchall-throw_ref-1") (param i32) (result i32) + (block $h (result exnref) + (try_table (result i32) (catch_all_ref $h) (throw $e0)) + (return) + ) + (if (param exnref) (i32.eqz (local.get 0)) + (then (throw_ref)) + (else (drop)) + ) + (i32.const 23) + ) + + (func (export "throw_ref-nested") (param i32) (result i32) + (local $exn1 exnref) + (local $exn2 exnref) + (block $h1 (result exnref) + (try_table (result i32) (catch_ref $e1 $h1) (throw $e1)) + (return) + ) + (local.set $exn1) + (block $h2 (result exnref) + (try_table (result i32) (catch_ref $e0 $h2) (throw $e0)) + (return) + ) + (local.set $exn2) + (if (i32.eq (local.get 0) (i32.const 0)) + (then (throw_ref (local.get $exn1))) + ) + (if (i32.eq (local.get 0) (i32.const 1)) + (then (throw_ref (local.get $exn2))) + ) + (i32.const 23) + ) + + (func (export "throw_ref-recatch") (param i32) (result i32) + (local $e exnref) + (block $h1 (result exnref) + (try_table (result i32) (catch_ref $e0 $h1) (throw $e0)) + (return) + ) + (local.set $e) + (block $h2 (result exnref) + (try_table (result i32) (catch_ref $e0 $h2) + (if (i32.eqz (local.get 0)) + (then (throw_ref (local.get $e))) + ) + (i32.const 42) + ) + (return) + ) + (drop) (i32.const 23) + ) + + (func (export "throw_ref-stack-polymorphism") + (local $e exnref) + (block $h (result exnref) + (try_table (result f64) (catch_ref $e0 $h) (throw $e0)) + (unreachable) + ) + (local.set $e) + (i32.const 1) + (throw_ref (local.get $e)) + ) +) + +(assert_exception (invoke "catch-throw_ref-0")) + +(assert_exception (invoke "catch-throw_ref-1" (i32.const 0))) +(assert_return (invoke "catch-throw_ref-1" (i32.const 1)) (i32.const 23)) + +(assert_exception (invoke "catchall-throw_ref-0")) + +(assert_exception (invoke "catchall-throw_ref-1" (i32.const 0))) +(assert_return (invoke "catchall-throw_ref-1" (i32.const 1)) (i32.const 23)) +(assert_exception (invoke "throw_ref-nested" (i32.const 0))) +(assert_exception (invoke "throw_ref-nested" (i32.const 1))) +(assert_return (invoke "throw_ref-nested" (i32.const 2)) (i32.const 23)) + +(assert_return (invoke "throw_ref-recatch" (i32.const 0)) (i32.const 23)) +(assert_return (invoke "throw_ref-recatch" (i32.const 1)) (i32.const 42)) + +(assert_exception (invoke "throw_ref-stack-polymorphism")) + +(assert_invalid (module (func (throw_ref))) "type mismatch") +(assert_invalid (module (func (block (throw_ref)))) "type mismatch") diff --git a/test/core/try_table.wast b/test/core/try_table.wast new file mode 100644 index 00000000..e64b6c18 --- /dev/null +++ b/test/core/try_table.wast @@ -0,0 +1,372 @@ +;; Test try-catch blocks. + +(module + (tag $e0 (export "e0")) + (func (export "throw") (throw $e0)) +) + +(register "test") + +(module + (tag $imported-e0 (import "test" "e0")) + (func $imported-throw (import "test" "throw")) + (tag $e0) + (tag $e1) + (tag $e2) + (tag $e-i32 (param i32)) + (tag $e-f32 (param f32)) + (tag $e-i64 (param i64)) + (tag $e-f64 (param f64)) + + (func $throw-if (param i32) (result i32) + (local.get 0) + (i32.const 0) (if (i32.ne) (then (throw $e0))) + (i32.const 0) + ) + + (func (export "simple-throw-catch") (param i32) (result i32) + (block $h + (try_table (result i32) (catch $e0 $h) + (if (i32.eqz (local.get 0)) (then (throw $e0)) (else)) + (i32.const 42) + ) + (return) + ) + (i32.const 23) + ) + + (func (export "unreachable-not-caught") + (block $h + (try_table (catch_all $h) (unreachable)) + (return) + ) + ) + + (func $div (param i32 i32) (result i32) + (local.get 0) (local.get 1) (i32.div_u) + ) + (func (export "trap-in-callee") (param i32 i32) (result i32) + (block $h + (try_table (result i32) (catch_all $h) + (call $div (local.get 0) (local.get 1)) + ) + (return) + ) + (i32.const 11) + ) + + (func (export "catch-complex-1") (param i32) (result i32) + (block $h1 + (try_table (result i32) (catch $e1 $h1) + (block $h0 + (try_table (result i32) (catch $e0 $h0) + (if (i32.eqz (local.get 0)) + (then (throw $e0)) + (else + (if (i32.eq (local.get 0) (i32.const 1)) + (then (throw $e1)) + (else (throw $e2)) + ) + ) + ) + (i32.const 2) + ) + (br 1) + ) + (i32.const 3) + ) + (return) + ) + (i32.const 4) + ) + + (func (export "catch-complex-2") (param i32) (result i32) + (block $h0 + (block $h1 + (try_table (result i32) (catch $e0 $h0) (catch $e1 $h1) + (if (i32.eqz (local.get 0)) + (then (throw $e0)) + (else + (if (i32.eq (local.get 0) (i32.const 1)) + (then (throw $e1)) + (else (throw $e2)) + ) + ) + ) + (i32.const 2) + ) + (return) + ) + (return (i32.const 4)) + ) + (i32.const 3) + ) + + (func (export "throw-catch-param-i32") (param i32) (result i32) + (block $h (result i32) + (try_table (result i32) (catch $e-i32 $h) + (throw $e-i32 (local.get 0)) + (i32.const 2) + ) + (return) + ) + (return) + ) + + (func (export "throw-catch-param-f32") (param f32) (result f32) + (block $h (result f32) + (try_table (result f32) (catch $e-f32 $h) + (throw $e-f32 (local.get 0)) + (f32.const 0) + ) + (return) + ) + (return) + ) + + (func (export "throw-catch-param-i64") (param i64) (result i64) + (block $h (result i64) + (try_table (result i64) (catch $e-i64 $h) + (throw $e-i64 (local.get 0)) + (i64.const 2) + ) + (return) + ) + (return) + ) + + (func (export "throw-catch-param-f64") (param f64) (result f64) + (block $h (result f64) + (try_table (result f64) (catch $e-f64 $h) + (throw $e-f64 (local.get 0)) + (f64.const 0) + ) + (return) + ) + (return) + ) + + (func (export "throw-catch_ref-param-i32") (param i32) (result i32) + (block $h (result i32 exnref) + (try_table (result i32) (catch_ref $e-i32 $h) + (throw $e-i32 (local.get 0)) + (i32.const 2) + ) + (return) + ) + (drop) (return) + ) + + (func (export "throw-catch_ref-param-f32") (param f32) (result f32) + (block $h (result f32 exnref) + (try_table (result f32) (catch_ref $e-f32 $h) + (throw $e-f32 (local.get 0)) + (f32.const 0) + ) + (return) + ) + (drop) (return) + ) + + (func (export "throw-catch_ref-param-i64") (param i64) (result i64) + (block $h (result i64 exnref) + (try_table (result i64) (catch_ref $e-i64 $h) + (throw $e-i64 (local.get 0)) + (i64.const 2) + ) + (return) + ) + (drop) (return) + ) + + (func (export "throw-catch_ref-param-f64") (param f64) (result f64) + (block $h (result f64 exnref) + (try_table (result f64) (catch_ref $e-f64 $h) + (throw $e-f64 (local.get 0)) + (f64.const 0) + ) + (return) + ) + (drop) (return) + ) + + (func $throw-param-i32 (param i32) (throw $e-i32 (local.get 0))) + (func (export "catch-param-i32") (param i32) (result i32) + (block $h (result i32) + (try_table (result i32) (catch $e-i32 $h) + (i32.const 0) + (call $throw-param-i32 (local.get 0)) + ) + (return) + ) + ) + + (func (export "catch-imported") (result i32) + (block $h + (try_table (result i32) (catch $imported-e0 $h) + (call $imported-throw (i32.const 1)) + ) + (return) + ) + (i32.const 2) + ) + + (func (export "catchless-try") (param i32) (result i32) + (block $h + (try_table (result i32) (catch $e0 $h) + (try_table (result i32) (call $throw-if (local.get 0))) + ) + (return) + ) + (i32.const 1) + ) + + (func $throw-void (throw $e0)) + (func (export "return-call-in-try-catch") + (block $h + (try_table (catch $e0 $h) + (return_call $throw-void) + ) + ) + ) + + (table funcref (elem $throw-void)) + (func (export "return-call-indirect-in-try-catch") + (block $h + (try_table (catch $e0 $h) + (return_call_indirect (i32.const 0)) + ) + ) + ) +) + +(assert_return (invoke "simple-throw-catch" (i32.const 0)) (i32.const 23)) +(assert_return (invoke "simple-throw-catch" (i32.const 1)) (i32.const 42)) + +(assert_trap (invoke "unreachable-not-caught") "unreachable") + +(assert_return (invoke "trap-in-callee" (i32.const 7) (i32.const 2)) (i32.const 3)) +(assert_trap (invoke "trap-in-callee" (i32.const 1) (i32.const 0)) "integer divide by zero") + +(assert_return (invoke "catch-complex-1" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "catch-complex-1" (i32.const 1)) (i32.const 4)) +(assert_exception (invoke "catch-complex-1" (i32.const 2))) + +(assert_return (invoke "catch-complex-2" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "catch-complex-2" (i32.const 1)) (i32.const 4)) +(assert_exception (invoke "catch-complex-2" (i32.const 2))) + +(assert_return (invoke "throw-catch-param-i32" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "throw-catch-param-i32" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "throw-catch-param-i32" (i32.const 10)) (i32.const 10)) + +(assert_return (invoke "throw-catch-param-f32" (f32.const 5.0)) (f32.const 5.0)) +(assert_return (invoke "throw-catch-param-f32" (f32.const 10.5)) (f32.const 10.5)) + +(assert_return (invoke "throw-catch-param-i64" (i64.const 5)) (i64.const 5)) +(assert_return (invoke "throw-catch-param-i64" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "throw-catch-param-i64" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "throw-catch-param-f64" (f64.const 5.0)) (f64.const 5.0)) +(assert_return (invoke "throw-catch-param-f64" (f64.const 10.5)) (f64.const 10.5)) + +(assert_return (invoke "throw-catch_ref-param-i32" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "throw-catch_ref-param-i32" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "throw-catch_ref-param-i32" (i32.const 10)) (i32.const 10)) + +(assert_return (invoke "throw-catch_ref-param-f32" (f32.const 5.0)) (f32.const 5.0)) +(assert_return (invoke "throw-catch_ref-param-f32" (f32.const 10.5)) (f32.const 10.5)) + +(assert_return (invoke "throw-catch_ref-param-i64" (i64.const 5)) (i64.const 5)) +(assert_return (invoke "throw-catch_ref-param-i64" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "throw-catch_ref-param-i64" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "throw-catch_ref-param-f64" (f64.const 5.0)) (f64.const 5.0)) +(assert_return (invoke "throw-catch_ref-param-f64" (f64.const 10.5)) (f64.const 10.5)) + +(assert_return (invoke "catch-param-i32" (i32.const 5)) (i32.const 5)) + +(assert_return (invoke "catch-imported") (i32.const 2)) + +(assert_return (invoke "catchless-try" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "catchless-try" (i32.const 1)) (i32.const 1)) + +(assert_exception (invoke "return-call-in-try-catch")) +(assert_exception (invoke "return-call-indirect-in-try-catch")) + +(module + (func $imported-throw (import "test" "throw")) + (tag $e0) + + (func (export "imported-mismatch") (result i32) + (block $h + (try_table (result i32) (catch_all $h) + (block $h0 + (try_table (result i32) (catch $e0 $h0) + (i32.const 1) + (call $imported-throw) + ) + (return) + ) + (i32.const 2) + ) + (return) + ) + (i32.const 3) + ) +) + +(assert_return (invoke "imported-mismatch") (i32.const 3)) + +(assert_malformed + (module quote "(module (func (catch_all)))") + "unexpected token" +) + +(assert_malformed + (module quote "(module (tag $e) (func (catch $e)))") + "unexpected token" +) + +(module + (tag $e) + (func (try_table (catch $e 0) (catch $e 0))) + (func (try_table (catch_all 0) (catch $e 0))) + (func (try_table (catch_all 0) (catch_all 0))) + (func (result exnref) (try_table (catch_ref $e 0) (catch_ref $e 0)) (unreachable)) + (func (result exnref) (try_table (catch_all_ref 0) (catch_ref $e 0)) (unreachable)) + (func (result exnref) (try_table (catch_all_ref 0) (catch_all_ref 0)) (unreachable)) +) + +(assert_invalid + (module (func (result i32) (try_table (result i32)))) + "type mismatch" +) +(assert_invalid + (module (func (result i32) (try_table (result i32) (i64.const 42)))) + "type mismatch" +) + +(assert_invalid + (module (tag) (func (try_table (catch_ref 0 0)))) + "type mismatch" +) +(assert_invalid + (module (tag) (func (result exnref) (try_table (catch 0 0)) (unreachable))) + "type mismatch" +) +(assert_invalid + (module (func (try_table (catch_all_ref 0)))) + "type mismatch" +) +(assert_invalid + (module (func (result exnref) (try_table (catch_all 0)) (unreachable))) + "type mismatch" +) +(assert_invalid + (module + (tag (param i64)) + (func (result i32 exnref) (try_table (result i32) (catch_ref 0 0) (i32.const 42))) + ) + "type mismatch" +) diff --git a/test/core/rethrow.wast b/test/legacy/exceptions/rethrow.wast similarity index 100% rename from test/core/rethrow.wast rename to test/legacy/exceptions/rethrow.wast diff --git a/test/legacy/exceptions/throw.wast b/test/legacy/exceptions/throw.wast new file mode 100644 index 00000000..d53b5b55 --- /dev/null +++ b/test/legacy/exceptions/throw.wast @@ -0,0 +1,51 @@ +;; Test throw instruction. + +(module + (tag $e0) + (tag $e-i32 (param i32)) + (tag $e-f32 (param f32)) + (tag $e-i64 (param i64)) + (tag $e-f64 (param f64)) + (tag $e-i32-i32 (param i32 i32)) + + (func $throw-if (export "throw-if") (param i32) (result i32) + (local.get 0) + (i32.const 0) (if (i32.ne) (then (throw $e0))) + (i32.const 0) + ) + + (func (export "throw-param-f32") (param f32) (local.get 0) (throw $e-f32)) + + (func (export "throw-param-i64") (param i64) (local.get 0) (throw $e-i64)) + + (func (export "throw-param-f64") (param f64) (local.get 0) (throw $e-f64)) + + (func $throw-1-2 (i32.const 1) (i32.const 2) (throw $e-i32-i32)) + (func (export "test-throw-1-2") + (try + (do (call $throw-1-2)) + (catch $e-i32-i32 + (i32.const 2) + (if (i32.ne) (then (unreachable))) + (i32.const 1) + (if (i32.ne) (then (unreachable))) + ) + ) + ) +) + +(assert_return (invoke "throw-if" (i32.const 0)) (i32.const 0)) +(assert_exception (invoke "throw-if" (i32.const 10))) +(assert_exception (invoke "throw-if" (i32.const -1))) + +(assert_exception (invoke "throw-param-f32" (f32.const 5.0))) +(assert_exception (invoke "throw-param-i64" (i64.const 5))) +(assert_exception (invoke "throw-param-f64" (f64.const 5.0))) + +(assert_return (invoke "test-throw-1-2")) + +(assert_invalid (module (func (throw 0))) "unknown tag 0") +(assert_invalid (module (tag (param i32)) (func (throw 0))) + "type mismatch: instruction requires [i32] but stack has []") +(assert_invalid (module (tag (param i32)) (func (i64.const 5) (throw 0))) + "type mismatch: instruction requires [i32] but stack has [i64]") diff --git a/test/core/try_catch.wast b/test/legacy/exceptions/try_catch.wast similarity index 100% rename from test/core/try_catch.wast rename to test/legacy/exceptions/try_catch.wast diff --git a/test/core/try_delegate.wast b/test/legacy/exceptions/try_delegate.wast similarity index 100% rename from test/core/try_delegate.wast rename to test/legacy/exceptions/try_delegate.wast diff --git a/test/legacy/run.py b/test/legacy/run.py new file mode 100755 index 00000000..f727aeef --- /dev/null +++ b/test/legacy/run.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +from __future__ import print_function +import argparse +import os +import os.path +import unittest +import subprocess +import glob +import sys + + +ownDir = os.path.dirname(os.path.abspath(sys.argv[0])) +inputDir = ownDir +outputDir = os.path.join(inputDir, "_output") + +parser = argparse.ArgumentParser() +parser.add_argument("--wasm", metavar="", default=os.path.join(os.getcwd(), "wasm")) +parser.add_argument("--js", metavar="") +parser.add_argument("--generate-js-only", action='store_true') +parser.add_argument("--out", metavar="", default=outputDir) +parser.add_argument("file", nargs='*') +arguments = parser.parse_args() +sys.argv = sys.argv[:1] + +exceptions_test_files = glob.glob(os.path.join(inputDir, "exceptions", "*.wast")) + +wasmCommand = arguments.wasm +jsCommand = arguments.js +generateJsOnly = arguments.generate_js_only +outputDir = arguments.out +inputFiles = arguments.file if arguments.file else exceptions_test_files + +if not os.path.exists(wasmCommand): + sys.stderr.write("""\ +Error: The executable '%s' does not exist. +Provide the correct path with the '--wasm' flag. + +""" % (wasmCommand)) + parser.print_help() + sys.exit(1) + + +class RunTests(unittest.TestCase): + def _runCommand(self, command, logPath, expectedExitCode = 0): + with open(logPath, 'w+') as out: + exitCode = subprocess.call(command, shell=True, stdout=out, stderr=subprocess.STDOUT) + self.assertEqual(expectedExitCode, exitCode, "failed with exit code %i (expected %i) for %s" % (exitCode, expectedExitCode, command)) + + def _auxFile(self, path): + if os.path.exists(path): + os.remove(path) + return path + + def _compareFile(self, expectFile, actualFile): + if os.path.exists(expectFile): + with open(expectFile) as expect: + with open(actualFile) as actual: + expectText = expect.read() + actualText = actual.read() + self.assertEqual(expectText, actualText) + + def _runTestFile(self, inputPath): + dir, inputFile = os.path.split(inputPath) + outputPath = os.path.join(outputDir, inputFile) + + # Generate JS first, then return early if we are only generating JS. + jsPath = self._auxFile(outputPath.replace(".wast", ".js")) + logPath = self._auxFile(jsPath + ".log") + self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, jsPath), logPath) + + if generateJsOnly: + return + + # Run original file + expectedExitCode = 1 if ".fail." in inputFile else 0 + logPath = self._auxFile(outputPath + ".log") + self._runCommand(('%s "%s"') % (wasmCommand, inputPath), logPath, expectedExitCode) + + if expectedExitCode != 0: + return + + # Convert to binary and run again + wasmPath = self._auxFile(outputPath + ".bin.wast") + logPath = self._auxFile(wasmPath + ".log") + self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, wasmPath), logPath) + self._runCommand(('%s "%s"') % (wasmCommand, wasmPath), logPath) + + # Convert back to text and run again + wastPath = self._auxFile(wasmPath + ".wast") + logPath = self._auxFile(wastPath + ".log") + self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wasmPath, wastPath), logPath) + self._runCommand(('%s "%s"') % (wasmCommand, wastPath), logPath) + + # Convert back to binary once more and compare + wasm2Path = self._auxFile(wastPath + ".bin.wast") + logPath = self._auxFile(wasm2Path + ".log") + self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wastPath, wasm2Path), logPath) + self._compareFile(wasmPath, wasm2Path) + + # Convert back to text once more and compare + wast2Path = self._auxFile(wasm2Path + ".wast") + logPath = self._auxFile(wast2Path + ".log") + self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wasm2Path, wast2Path), logPath) + self._compareFile(wastPath, wast2Path) + + if jsCommand != None: + self._runCommand(('%s "%s"') % (jsCommand, jsPath), logPath) + + +if __name__ == "__main__": + if not os.path.exists(outputDir): + os.makedirs(outputDir) + for fileName in inputFiles: + testName = 'test ' + os.path.basename(fileName) + setattr(RunTests, testName, lambda self, file=fileName: self._runTestFile(file)) + unittest.main()