Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit f87ba21

Browse files
authored
Merge pull request #33 from KarlSchimpf/layer1
Initial draft of the layer 1.
2 parents ddf3e5a + d2ff7d8 commit f87ba21

File tree

1 file changed

+390
-0
lines changed

1 file changed

+390
-0
lines changed

proposals/Layer-1.md

Lines changed: 390 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,390 @@
1+
# Layer 1 exception handling
2+
3+
Layer 1 of exception handling is the MVP (minimal viable proposal) for
4+
implementing exceptions in WebAssembly. As such, it doesn't include higher-level
5+
concepts and structures. These concept and structures are introduced in later
6+
layers, and either:
7+
8+
1. Improves readability by combining concepts in layer 1 into higher-level
9+
constructs, thereby reducing code size.
10+
11+
2. Allow performance improvements in the VM.
12+
13+
3. Introduce additional new functionality not available in layer 1.
14+
15+
## Overview
16+
17+
Exception handling allows code to break control flow when an exception is
18+
thrown. The exeception can be any exception known by the WebAssembly module, or
19+
it may an unknown exception that was thrown by a called imported function.
20+
21+
One of the problems with exception handling is that both WebAssembly and the
22+
host VM probably have different notions of what exceptions are, but both must be
23+
aware of the other.
24+
25+
It is difficult to define exceptions in WebAssembly because (in general)
26+
it doesn't have knowledge of the host VM. Further, adding such knowledge to
27+
WebAssembly would limit the ability for other host VMs to support WebAssembly
28+
exceptions.
29+
30+
One issue is that both sides need to know if an exception was thrown by the
31+
other, because cleanup may need to be performed.
32+
33+
Another problem is that WebAssembly doesn't have direct access to the host VM's
34+
memory. As a result, WebAssembly defers the handling of exceptions to the host
35+
VM.
36+
37+
To access exceptions, WebAssembly provides instructions to check if the
38+
exception is one that WebAssembly understands. If so, the data of the
39+
WebAssembly exceptions's data is extracted and copied onto the stack, allowing
40+
succeeding instructions to process the data.
41+
42+
Lastly, exception lifetimes must be maintained by the host VM, so that it can
43+
collect and reuse the memory used by exceptions. This implies that the host must
44+
know where exceptions are stored, so that it can determine when an exception can
45+
be garbage collected.
46+
47+
This also implies that the host VM must provide a garbage collector for
48+
exceptions. For host VMs that have garbage collection (such as JavaScript),
49+
this is not a problem.
50+
51+
However, not all host VMs may have a garbage collector. For this reason,
52+
WebAssembly exceptions are designed to allow the use of reference counters to
53+
perform the the garbage collection in the host VM.
54+
55+
To do this, WebAssembly exceptions are immutable once created, to avoid cyclic
56+
data structures that can't be garbage collected. It also means that exceptions
57+
can't be stored into linear memory. The rationale for this is twofold:
58+
59+
* For security. Loads
60+
and stores do not guarantee that the data read was of the same type as
61+
stored. This allows spoofing of exception references that may allow a
62+
WebAssembly module to access data it should not know in the host VM.
63+
64+
* The host VM does not know the layout of data in linear memory, so it can't
65+
find places where exception references are stored.
66+
67+
Hence, while an exception reference is a new first class type, this proposal
68+
disallows their usage in linear memory.
69+
70+
A WebAssembly exception is created by the `except` instruction. Once an
71+
exception is created, you can throw it with the `throw` instruction. Thrown
72+
exceptions are handled as follows:
73+
74+
1. They can be caught by a catch block in an enclosing try block of a function
75+
body. The caught exception is pushed onto the stack.
76+
77+
1. Throws not caught within a function body continue up the call stack, popping
78+
call frames, until an enclosing try block is found.
79+
80+
1. If the call stack is exhausted without any enclosing try blocks, the host VM
81+
defines how to handle the uncaught exception.
82+
83+
### Exceptions
84+
85+
An `exception` is an internal construct in WebAssembly that is maintained by the
86+
host. WebAssembly exceptions (as opposed to host exceptions) are defined by a
87+
new `exception section` of a WebAssembly module. The exception section is a list
88+
of exception types, from which exceptions can be created.
89+
90+
Each exception type has a `type signature`. The type signature defines the list
91+
of values associated with the exception.
92+
93+
Within the module, exception types are identified by an index into the
94+
[exception index space](#exception-index-space). This index is referred to as
95+
the `exception tag`. The `tagged exception type` is the corresponding
96+
exception type refered to by the exception tag.
97+
98+
Exception types can be imported and exported by adding the appropriate entries
99+
to the import and export sections of the module. All imported/exported exception
100+
types must be named to reconcile exception tags between modules.
101+
102+
Exception tags are used by:
103+
104+
1. The `except` instruction which creates a WebAssembly instance of the
105+
corresponding tagged exception type, and pushes a reference to it onto the
106+
stack.
107+
108+
2. The `if_except` instruction that queries an exception to see if it is an
109+
instance of the corresponding tagged exception class, and if true it pushes
110+
the corresponding values of the exception onto the stack.
111+
112+
### The exception refernce data type
113+
114+
Data types are extended to have a new `except_ref` type. The representation of
115+
an exception type is left to the host VM, but its size must be fixed. The actual
116+
number of bytes it takes to represent any `except_ref` is left to the host VM.
117+
118+
### Try and catch blocks
119+
120+
A _try block_ defines a list of instructions that may need to process exceptions
121+
and/or clean up state when an exception is thrown. Like other higher-level
122+
constructs, a try block begins with a `try` instruction, and ends with an `end`
123+
instruction. That is, a try block is sequence of instructions having the
124+
following form:
125+
126+
```
127+
try block_type
128+
instruction*
129+
catch
130+
instruction*
131+
end
132+
```
133+
134+
A try block ends with a `catch block` that is defined by the list of
135+
instructions after the `catch` instruction.
136+
137+
Try blocks, like control-flow blocks, have a _block type_. The block type of a
138+
try block defines the values yielded by the evaluation the try block when either
139+
no exception is thrown, or the exception is successfully caught by the catch
140+
block.
141+
142+
In the initial implementation, try blocks may only yield 0 or 1 values.
143+
144+
### Exception creation
145+
146+
A `except` instruction has a single immediate argument, an exception tag. The
147+
corresponding tagged exception type is used to define the data fields of the
148+
created exception. The values for the data fields must be on top of the operand
149+
stack, and must correspond to the exception's type signature. These values are
150+
popped off the stack and an instance of the exception is then created. A
151+
reference to the created exception is then pushed onto the stack.
152+
153+
### Throws
154+
155+
The `throw` throws the exception on top of the stack. The exception is popped
156+
off the top of the stack before throwing.
157+
158+
When an exception is thrown, the host VM searches for nearest enclosing try
159+
block body that execution is in. That try block is called the _catching_ try
160+
block.
161+
162+
If the throw appears within the body of a try block, it is the catching try
163+
block.
164+
165+
If a throw occurs within a function body, and it doesn't appear inside the body
166+
of a try block, the throw continues up the call stack until it is in the body of
167+
an an enclosing try block, or the call stack is flushed. If the call stack is
168+
flushed, the host VM defines how to handle uncaught exceptions. Otherwise, the
169+
found enclosing try block is the catching try block.
170+
171+
A throw inside the body of a catch block is never caught by the corresponding
172+
try block of the catch block, since instructions in the body of the catch block
173+
are not in the body of the try block.
174+
175+
Once a catching try block is found for the throw, the operand stack is popped back
176+
to the size the operand stack had when the try block was entered, and then
177+
the caught exception is pushed onto the stack.
178+
179+
If control is transferred to the body of a catch block, and the last instruction
180+
in the body is executed, control then exits the try block.
181+
182+
If the selected catch block does not throw an exception, it must yield the
183+
value(s) expected by the corresponding catching try block. This includes popping
184+
the caught exception.
185+
186+
Note that a caught exception can be rethrown using the `throw` instruction.
187+
188+
### Exception data extraction
189+
190+
The `if_except block` defines a conditional query of the exception on top of the
191+
stack. The exception is not popped when queried. The if_except block has two
192+
subblocks, the `then` and `else` subblocks, like that of an `if` block. The then
193+
block is a sequence of instructions following the `if_except` instruction. The
194+
else block is optional, and if it appears, it begins with the `else`
195+
instruction. The scope of the if_except block is from the `if_except`
196+
instruction to the corresponding `end` instruction.
197+
198+
That is, the forms of an if_except block is:
199+
200+
```
201+
if_except block_type except_index
202+
Instruction*
203+
end
204+
205+
if_except block_type except_index
206+
Instruction*
207+
else
208+
Instructions*
209+
end
210+
```
211+
212+
The conditional query of an exception succeeds when the exception on the top of
213+
the stack is an instance of the corresponding tagged exception type (defined by
214+
`except_index`).
215+
216+
If the query succeeds, the data values (associated with the type signature of
217+
the exception class) are extracted and pushed onto the stack, and control
218+
transfers to the instructions in the then block.
219+
220+
If the query fails, it either enters the else block, or transfer control to the
221+
end of the if_except block if there is no else block.
222+
223+
### Debugging
224+
225+
Earlier discussion implied that when an exception is thrown, the runtime will
226+
pop the operand stack across function calls until a corresponding, enclosing try
227+
block is found. The implementation may actually not do this. Rather, it may
228+
first search up the call stack to see if there is an enclosing try. If none are
229+
found, it could terminate the thread at the point of the throw. This would
230+
allow better debugging capability, since the corresponding call stack is still
231+
there to query.
232+
233+
## Changes to the text format.
234+
235+
This section describes change in the
236+
[instruction syntax document](https://github.com/WebAssembly/spec/blob/master/document/core/instructions.rst).
237+
238+
### New instructions
239+
240+
The following rules are added to *instructions*:
241+
242+
```
243+
try resulttype instructions* catch instructions* end |
244+
except except_index |
245+
throw |
246+
if_except resulttype except_index then Instructions* end |
247+
if_except resulttype except_index then Instructions* else Instructions* end
248+
```
249+
250+
Like the `block`, `loop`, and `if` instructions, the `try` and `if_except`
251+
instructions are *structured* control flow instructions, and can be
252+
labeled. This allows branch instructions to exit try and `if_except` blocks.
253+
254+
The `except_index` of the `except` and `if_except` instructions defines the
255+
exception type to create/extract form. See [exception index
256+
space](#exception-index-space) for further clarification of exception tags.
257+
258+
## Changes to Modules document.
259+
260+
This section describes change in the
261+
[Modules document](https://github.com/WebAssembly/design/blob/master/Modules.md).
262+
263+
### Exception index space
264+
265+
The _exception index space_ indexes all imported and internally-defined
266+
exceptions, assigning monotonically-increasing indices based on the order
267+
defined in the import and exception sections. Thus, the index space starts at
268+
zero with imported exceptions followed by internally-defined exceptions in
269+
the [exception section](#exception-section).
270+
271+
## Changes to the binary model
272+
273+
This section describes changes in
274+
the
275+
[binary encoding design document](https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md).
276+
277+
278+
### Data Types
279+
280+
#### except_ref
281+
282+
An exception reference pointing to an instance of an exception. The size
283+
is fixed, but unknown in WebAssembly (the host defines the size in bytes).
284+
285+
### Language Types
286+
287+
| Opcode | Type constructor |
288+
|--------|------------------|
289+
| -0x41 | `except_ref` |
290+
291+
#### value_type
292+
293+
A `varint7` indicating a a `value type` is extended to include `except_ref` as
294+
encoded above.
295+
296+
#### Other Types
297+
298+
##### except_type
299+
300+
An exception is described by its exception type signature, which corresponds to
301+
the data fields of the exception.
302+
303+
| Field | Type | Description |
304+
|-------|------|-------------|
305+
| `count` | `varuint32` | The number of types in the signature |
306+
| `type` | `value_type*` | The type of each element in the signature |
307+
308+
309+
##### external_kind
310+
311+
A single-byte unsigned integer indicating the kind of definition being imported
312+
or defined:
313+
314+
* `0` indicating a `Function` [import](Modules.md#imports) or [definition](Modules.md#function-and-code-sections)
315+
* `1` indicating a `Table` [import](Modules.md#imports) or [definition](Modules.md#table-section)
316+
* `2` indicating a `Memory` [import](Modules.md#imports) or [definition](Modules.md#linear-memory-section)
317+
* `3` indicating a `Global` [import](Modules.md#imports) or [definition](Modules.md#global-section)
318+
* `4` indicating an `Exception` [import](#import-section) or [definition](#exception-sectio)
319+
320+
### Module structure
321+
322+
#### High-level structure
323+
324+
A new `exception` section is introduced and is named `exception`. If included,
325+
it must appear between the `Export` and `Start` sections of the module.
326+
327+
328+
##### Exception section
329+
330+
The `exception` section is the named section 'exception'. The exception section
331+
declares exception types using exception type signatures.
332+
333+
| Field | Type | Description |
334+
|-------|------|-------------|
335+
| count | `varuint32` | count of the number of exceptions to follow |
336+
| sig | `except_type*` | The type signature of the data fields for each exception |
337+
338+
339+
##### Import section
340+
341+
The import section is extended to include exception types by extending an
342+
`import_entry` as follows:
343+
344+
If the `kind` is `Exception`:
345+
346+
| Field | Type | Description |
347+
|-------|------|-------------|
348+
| `sig` | `except_type` | the type signature of the exception |
349+
350+
##### Export section
351+
352+
The export section is extended to include exception types by extending an
353+
`export_entry` as follows:
354+
355+
If the `kind` is `Exception`, then the `index` is into the corresponding
356+
exception in the [exception index space](#exception-index-space).
357+
358+
359+
##### Name section
360+
361+
The set of known values for `name_type` of a name section is extended as
362+
follows:
363+
364+
| Name Type | Code | Description |
365+
| --------- | ---- | ----------- |
366+
| [Function](#function-names) | `1` | Assigns names to functions |
367+
| [Local](#local-names) | `2` | Assigns names to locals in functions |
368+
| [Exception](#exception-names) | `3` | Assigns names to exception types |
369+
370+
###### Exception names
371+
372+
The exception names subsection is a `name_map` which assigns names to a subset
373+
of the _exception_ indices from the exception section. (Used for both imports
374+
and module-defined).
375+
376+
### Control flow operators
377+
378+
The control flow operators are extended to define try blocks, catch blocks,
379+
throws, and rethrows as follows:
380+
381+
| Name | Opcode | Immediates | Description |
382+
| ---- | ---- | ---- | ---- |
383+
| `try` | `0x06` | sig : `block_type` | begins a block which can handle thrown exceptions |
384+
| `catch` | `0x07` | | begins the catch block of the try block |
385+
| `throw` | `0x08` | |Throws the exception on top of the stack |
386+
| `except` | `0x09` | tag : varuint32 | Creates an exception defined by the exception tag and pushes reference on stack |
387+
| `if_except` | `0x0a` | sig : `block_type` , tag : `varuint32` | Begin exception data extraction if exception on stack was created using the corresponding exception tag |
388+
389+
The *sig* fields of `block`, `if`, `try` and `if_except` operators are block
390+
signatures which describe their use of the operand stack.

0 commit comments

Comments
 (0)