Skip to content

Commit f5d9ee2

Browse files
committed
Reorder Context definitions to present top-down
1 parent 8d89078 commit f5d9ee2

File tree

3 files changed

+86
-73
lines changed

3 files changed

+86
-73
lines changed

design/mvp/CanonicalABI.md

+48-42
Original file line numberDiff line numberDiff line change
@@ -210,31 +210,39 @@ The subsequent definitions of loading and storing a value from linear memory
210210
require additional context, which is threaded through most subsequent
211211
definitions via the `cx` parameter:
212212
```python
213+
class Context:
214+
opts: CanonicalOptions
215+
inst: ComponentInstance
216+
called_as_export: bool
217+
```
218+
219+
The `opts` field represents the [`canonopt`] values supplied to
220+
currently-executing `canon lift` or `canon lower`:
221+
```python
213222
class CanonicalOptions:
214223
memory: bytearray
215224
string_encoding: str
216225
realloc: Callable[[int,int,int,int],int]
217226
post_return: Callable[[],None]
227+
```
218228

229+
The `inst` field represents the component instance that the currently-executing
230+
canonical definition is defined to execute inside. The `may_enter` and
231+
`may_leave` fields are used to enforce the [component invariants]: `may_leave`
232+
indicates whether the instance may call out to an import and the `may_enter`
233+
state indicates whether the instance may be called from the outside world
234+
through an export.
235+
```python
219236
class ComponentInstance:
220237
may_leave = True
221238
may_enter = True
222239
# ...
223-
224-
class Context:
225-
opts: CanonicalOptions
226-
inst: ComponentInstance
227240
```
228-
Going through the fields of `Context`:
229-
230-
The `opts` field represents the [`canonopt`] values supplied to
231-
currently-executing `canon lift` or `canon lower`.
232241

233-
The `inst` field represents the component instance that the currently-executing
234-
canonical definition is closed over. The `may_enter` and `may_leave` fields are
235-
used to enforce the [component invariants]: `may_leave` indicates whether the
236-
instance may call out to an import and the `may_enter` state indicates whether
237-
the instance may be called from the outside world through an export.
242+
Lastly, the `called_as_export` field indicates whether the lifted function is
243+
being called through a component export or whether this is an internal call,
244+
(for example, when a child component calls an import that is defined by its
245+
parent component).
238246

239247

240248
### Loading
@@ -1204,29 +1212,29 @@ component*.
12041212

12051213
Given the above closure arguments, `canon_lift` is defined:
12061214
```python
1207-
def canon_lift(callee_cx, callee, ft, args, called_as_export):
1208-
if called_as_export:
1209-
trap_if(not callee_cx.inst.may_enter)
1210-
callee_cx.inst.may_enter = False
1215+
def canon_lift(cx, callee, ft, args):
1216+
if cx.called_as_export:
1217+
trap_if(not cx.inst.may_enter)
1218+
cx.inst.may_enter = False
12111219
else:
1212-
assert(not callee_cx.inst.may_enter)
1220+
assert(not cx.inst.may_enter)
12131221

1214-
assert(callee_cx.inst.may_leave)
1215-
callee_cx.inst.may_leave = False
1216-
flat_args = lower_values(callee_cx, MAX_FLAT_PARAMS, args, ft.param_types())
1217-
callee_cx.inst.may_leave = True
1222+
assert(cx.inst.may_leave)
1223+
cx.inst.may_leave = False
1224+
flat_args = lower_values(cx, MAX_FLAT_PARAMS, args, ft.param_types())
1225+
cx.inst.may_leave = True
12181226

12191227
try:
12201228
flat_results = callee(flat_args)
12211229
except CoreWebAssemblyException:
12221230
trap()
12231231

1224-
results = lift_values(callee_cx, MAX_FLAT_RESULTS, ValueIter(flat_results), ft.result_types())
1232+
results = lift_values(cx, MAX_FLAT_RESULTS, ValueIter(flat_results), ft.result_types())
12251233
def post_return():
1226-
if callee_cx.opts.post_return is not None:
1227-
callee_cx.opts.post_return(flat_results)
1228-
if called_as_export:
1229-
callee_cx.inst.may_enter = True
1234+
if cx.opts.post_return is not None:
1235+
cx.opts.post_return(flat_results)
1236+
if cx.called_as_export:
1237+
cx.inst.may_enter = True
12301238

12311239
return (results, post_return)
12321240
```
@@ -1237,15 +1245,13 @@ boundaries. Thus, if a component wishes to signal an error, it must use some
12371245
sort of explicit type such as `result` (whose `error` case particular language
12381246
bindings may choose to map to and from exceptions).
12391247

1240-
The `called_as_export` parameter indicates whether `canon_lift` is being called
1241-
as part of a component export or whether this `canon_lift` is being called
1242-
internally (for example, by a child component instance). By clearing
1243-
`may_enter` for the duration of `canon_lift` when called as an export, the
1244-
dynamic traps ensure that components cannot be reentered, which is a [component
1245-
invariant]. Furthermore, because `may_enter` is not cleared on the exceptional
1246-
exit path taken by `trap()`, if there is a trap during Core WebAssembly
1247-
execution or lifting/lowering, the component is left permanently un-enterable,
1248-
ensuring the lockdown-after-trap [component invariant].
1248+
By clearing `may_enter` for the duration of `canon_lift` when the function is
1249+
called as an export, the dynamic traps ensure that components cannot be
1250+
reentered, ensuring the non-reentrance [component invariant]. Furthermore,
1251+
because `may_enter` is not cleared on the exceptional exit path taken by
1252+
`trap()`, if there is a trap during Core WebAssembly execution of lifting or
1253+
lowering, the component is left permanently un-enterable, ensuring the
1254+
lockdown-after-trap [component invariant].
12491255

12501256
The contract assumed by `canon_lift` (and ensured by `canon_lower` below) is
12511257
that the caller of `canon_lift` *must* call `post_return` right after lowering
@@ -1272,17 +1278,17 @@ Thus, from the perspective of Core WebAssembly, `$f` is a [function instance]
12721278
containing a `hostfunc` that closes over `$opts`, `$inst`, `$callee` and `$ft`
12731279
and, when called from Core WebAssembly code, calls `canon_lower`, which is defined as:
12741280
```python
1275-
def canon_lower(caller_cx, callee, ft, flat_args):
1276-
trap_if(not caller_cx.inst.may_leave)
1281+
def canon_lower(cx, callee, ft, flat_args):
1282+
trap_if(not cx.inst.may_leave)
12771283

12781284
flat_args = ValueIter(flat_args)
1279-
args = lift_values(caller_cx, MAX_FLAT_PARAMS, flat_args, ft.param_types())
1285+
args = lift_values(cx, MAX_FLAT_PARAMS, flat_args, ft.param_types())
12801286

12811287
results, post_return = callee(args)
12821288

1283-
caller_cx.inst.may_leave = False
1284-
flat_results = lower_values(caller_cx, MAX_FLAT_RESULTS, results, ft.result_types(), flat_args)
1285-
caller_cx.inst.may_leave = True
1289+
cx.inst.may_leave = False
1290+
flat_results = lower_values(cx, MAX_FLAT_RESULTS, results, ft.result_types(), flat_args)
1291+
cx.inst.may_leave = True
12861292

12871293
post_return()
12881294

design/mvp/canonical-abi/definitions.py

+30-24
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Boilerplate
66

7+
from __future__ import annotations
78
import math
89
import struct
910
from dataclasses import dataclass
@@ -271,21 +272,26 @@ def num_i32_flags(labels):
271272

272273
### Context
273274

275+
class Context:
276+
opts: CanonicalOptions
277+
inst: ComponentInstance
278+
called_as_export: bool
279+
280+
#
281+
274282
class CanonicalOptions:
275283
memory: bytearray
276284
string_encoding: str
277285
realloc: Callable[[int,int,int,int],int]
278286
post_return: Callable[[],None]
279287

288+
#
289+
280290
class ComponentInstance:
281291
may_leave = True
282292
may_enter = True
283293
# ...
284294

285-
class Context:
286-
opts: CanonicalOptions
287-
inst: ComponentInstance
288-
289295
### Loading
290296

291297
def load(cx, ptr, t):
@@ -1002,45 +1008,45 @@ def lower_values(cx, max_flat, vs, ts, out_param = None):
10021008

10031009
### `lift`
10041010

1005-
def canon_lift(callee_cx, callee, ft, args, called_as_export):
1006-
if called_as_export:
1007-
trap_if(not callee_cx.inst.may_enter)
1008-
callee_cx.inst.may_enter = False
1011+
def canon_lift(cx, callee, ft, args):
1012+
if cx.called_as_export:
1013+
trap_if(not cx.inst.may_enter)
1014+
cx.inst.may_enter = False
10091015
else:
1010-
assert(not callee_cx.inst.may_enter)
1016+
assert(not cx.inst.may_enter)
10111017

1012-
assert(callee_cx.inst.may_leave)
1013-
callee_cx.inst.may_leave = False
1014-
flat_args = lower_values(callee_cx, MAX_FLAT_PARAMS, args, ft.param_types())
1015-
callee_cx.inst.may_leave = True
1018+
assert(cx.inst.may_leave)
1019+
cx.inst.may_leave = False
1020+
flat_args = lower_values(cx, MAX_FLAT_PARAMS, args, ft.param_types())
1021+
cx.inst.may_leave = True
10161022

10171023
try:
10181024
flat_results = callee(flat_args)
10191025
except CoreWebAssemblyException:
10201026
trap()
10211027

1022-
results = lift_values(callee_cx, MAX_FLAT_RESULTS, ValueIter(flat_results), ft.result_types())
1028+
results = lift_values(cx, MAX_FLAT_RESULTS, ValueIter(flat_results), ft.result_types())
10231029
def post_return():
1024-
if callee_cx.opts.post_return is not None:
1025-
callee_cx.opts.post_return(flat_results)
1026-
if called_as_export:
1027-
callee_cx.inst.may_enter = True
1030+
if cx.opts.post_return is not None:
1031+
cx.opts.post_return(flat_results)
1032+
if cx.called_as_export:
1033+
cx.inst.may_enter = True
10281034

10291035
return (results, post_return)
10301036

10311037
### `lower`
10321038

1033-
def canon_lower(caller_cx, callee, ft, flat_args):
1034-
trap_if(not caller_cx.inst.may_leave)
1039+
def canon_lower(cx, callee, ft, flat_args):
1040+
trap_if(not cx.inst.may_leave)
10351041

10361042
flat_args = ValueIter(flat_args)
1037-
args = lift_values(caller_cx, MAX_FLAT_PARAMS, flat_args, ft.param_types())
1043+
args = lift_values(cx, MAX_FLAT_PARAMS, flat_args, ft.param_types())
10381044

10391045
results, post_return = callee(args)
10401046

1041-
caller_cx.inst.may_leave = False
1042-
flat_results = lower_values(caller_cx, MAX_FLAT_RESULTS, results, ft.result_types(), flat_args)
1043-
caller_cx.inst.may_leave = True
1047+
cx.inst.may_leave = False
1048+
flat_results = lower_values(cx, MAX_FLAT_RESULTS, results, ft.result_types(), flat_args)
1049+
cx.inst.may_leave = True
10441050

10451051
post_return()
10461052

design/mvp/canonical-abi/run_tests.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ def realloc(self, original_ptr, original_size, alignment, new_size):
3232
self.memory[ret : ret + original_size] = self.memory[original_ptr : original_ptr + original_size]
3333
return ret
3434

35-
def mk_cx(memory, encoding = None, realloc = None, post_return = None):
35+
def mk_cx(memory = bytearray(), encoding = 'utf8', realloc = None, post_return = None):
3636
cx = Context()
3737
cx.opts = CanonicalOptions()
3838
cx.opts.memory = memory
3939
cx.opts.string_encoding = encoding
4040
cx.opts.realloc = realloc
4141
cx.opts.post_return = post_return
4242
cx.inst = ComponentInstance()
43+
cx.called_as_export = True
4344
return cx
4445

4546
def mk_str(s):
@@ -56,7 +57,7 @@ def fail(msg):
5657
raise BaseException(msg)
5758

5859
def test(t, vals_to_lift, v,
59-
cx = mk_cx(bytearray(), 'utf8', None, None),
60+
cx = mk_cx(),
6061
dst_encoding = None,
6162
lower_t = None,
6263
lower_v = None):
@@ -85,7 +86,7 @@ def test_name():
8586
heap = Heap(5*len(cx.opts.memory))
8687
if dst_encoding is None:
8788
dst_encoding = cx.opts.string_encoding
88-
cx = mk_cx(heap.memory, dst_encoding, heap.realloc, None)
89+
cx = mk_cx(heap.memory, dst_encoding, heap.realloc)
8990
lowered_vals = lower_flat(cx, v, lower_t)
9091
assert(flatten_type(lower_t) == list(map(lambda v: v.t, lowered_vals)))
9192

@@ -200,7 +201,7 @@ def test_nan64(inbits, outbits):
200201
def test_string_internal(src_encoding, dst_encoding, s, encoded, tagged_code_units):
201202
heap = Heap(len(encoded))
202203
heap.memory[:] = encoded[:]
203-
cx = mk_cx(heap.memory, src_encoding, None, None)
204+
cx = mk_cx(heap.memory, src_encoding)
204205
v = (s, src_encoding, tagged_code_units)
205206
test(String(), [0, tagged_code_units], v, cx, dst_encoding)
206207

@@ -237,7 +238,7 @@ def test_string(src_encoding, dst_encoding, s):
237238

238239
def test_heap(t, expect, args, byte_array):
239240
heap = Heap(byte_array)
240-
cx = mk_cx(heap.memory, 'utf8', None, None)
241+
cx = mk_cx(heap.memory)
241242
test(t, args, expect, cx)
242243

243244
test_heap(List(Record([])), [{},{},{}], [0,3], [])
@@ -348,10 +349,10 @@ def test_roundtrip(t, v):
348349

349350
callee_heap = Heap(1000)
350351
callee_cx = mk_cx(callee_heap.memory, 'utf8', callee_heap.realloc, lambda x: () )
351-
lifted_callee = lambda args: canon_lift(callee_cx, callee, ft, args, True)
352+
lifted_callee = lambda args: canon_lift(callee_cx, callee, ft, args)
352353

353354
caller_heap = Heap(1000)
354-
caller_cx = mk_cx(caller_heap.memory, 'utf8', caller_heap.realloc, None)
355+
caller_cx = mk_cx(caller_heap.memory, 'utf8', caller_heap.realloc)
355356

356357
flat_args = lower_flat(caller_cx, v, t)
357358
flat_results = canon_lower(caller_cx, lifted_callee, ft, flat_args)

0 commit comments

Comments
 (0)