Skip to content

Commit

Permalink
Move called_as_export into Context
Browse files Browse the repository at this point in the history
  • Loading branch information
lukewagner committed Dec 4, 2022
1 parent c489282 commit e80a1de
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 56 deletions.
62 changes: 33 additions & 29 deletions design/mvp/CanonicalABI.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ definitions via the `cx` parameter:
class Context:
opts: CanonicalOptions
inst: ComponentInstance
called_as_export: bool
```

The `opts` field represents the [`canonopt`] values supplied to
Expand All @@ -238,6 +239,11 @@ class ComponentInstance:
# ...
```

Lastly, the `called_as_export` field indicates whether the lifted function is
being called through a component export or whether this is an internal call,
(for example, when a child component calls an import that is defined by its
parent component).


### Loading

Expand Down Expand Up @@ -1206,29 +1212,29 @@ component*.

Given the above closure arguments, `canon_lift` is defined:
```python
def canon_lift(callee_cx, callee, ft, args, called_as_export):
if called_as_export:
trap_if(not callee_cx.inst.may_enter)
callee_cx.inst.may_enter = False
def canon_lift(cx, callee, ft, args):
if cx.called_as_export:
trap_if(not cx.inst.may_enter)
cx.inst.may_enter = False
else:
assert(not callee_cx.inst.may_enter)
assert(not cx.inst.may_enter)

assert(callee_cx.inst.may_leave)
callee_cx.inst.may_leave = False
flat_args = lower_values(callee_cx, MAX_FLAT_PARAMS, args, ft.param_types())
callee_cx.inst.may_leave = True
assert(cx.inst.may_leave)
cx.inst.may_leave = False
flat_args = lower_values(cx, MAX_FLAT_PARAMS, args, ft.param_types())
cx.inst.may_leave = True

try:
flat_results = callee(flat_args)
except CoreWebAssemblyException:
trap()

results = lift_values(callee_cx, MAX_FLAT_RESULTS, ValueIter(flat_results), ft.result_types())
results = lift_values(cx, MAX_FLAT_RESULTS, ValueIter(flat_results), ft.result_types())
def post_return():
if callee_cx.opts.post_return is not None:
callee_cx.opts.post_return(flat_results)
if called_as_export:
callee_cx.inst.may_enter = True
if cx.opts.post_return is not None:
cx.opts.post_return(flat_results)
if cx.called_as_export:
cx.inst.may_enter = True

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

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

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

flat_args = ValueIter(flat_args)
args = lift_values(caller_cx, MAX_FLAT_PARAMS, flat_args, ft.param_types())
args = lift_values(cx, MAX_FLAT_PARAMS, flat_args, ft.param_types())

results, post_return = callee(args)

caller_cx.inst.may_leave = False
flat_results = lower_values(caller_cx, MAX_FLAT_RESULTS, results, ft.result_types(), flat_args)
caller_cx.inst.may_leave = True
cx.inst.may_leave = False
flat_results = lower_values(cx, MAX_FLAT_RESULTS, results, ft.result_types(), flat_args)
cx.inst.may_leave = True

post_return()

Expand Down
41 changes: 21 additions & 20 deletions design/mvp/canonical-abi/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ def num_i32_flags(labels):
class Context:
opts: CanonicalOptions
inst: ComponentInstance
called_as_export: bool

#

Expand Down Expand Up @@ -1007,45 +1008,45 @@ def lower_values(cx, max_flat, vs, ts, out_param = None):

### `lift`

def canon_lift(callee_cx, callee, ft, args, called_as_export):
if called_as_export:
trap_if(not callee_cx.inst.may_enter)
callee_cx.inst.may_enter = False
def canon_lift(cx, callee, ft, args):
if cx.called_as_export:
trap_if(not cx.inst.may_enter)
cx.inst.may_enter = False
else:
assert(not callee_cx.inst.may_enter)
assert(not cx.inst.may_enter)

assert(callee_cx.inst.may_leave)
callee_cx.inst.may_leave = False
flat_args = lower_values(callee_cx, MAX_FLAT_PARAMS, args, ft.param_types())
callee_cx.inst.may_leave = True
assert(cx.inst.may_leave)
cx.inst.may_leave = False
flat_args = lower_values(cx, MAX_FLAT_PARAMS, args, ft.param_types())
cx.inst.may_leave = True

try:
flat_results = callee(flat_args)
except CoreWebAssemblyException:
trap()

results = lift_values(callee_cx, MAX_FLAT_RESULTS, ValueIter(flat_results), ft.result_types())
results = lift_values(cx, MAX_FLAT_RESULTS, ValueIter(flat_results), ft.result_types())
def post_return():
if callee_cx.opts.post_return is not None:
callee_cx.opts.post_return(flat_results)
if called_as_export:
callee_cx.inst.may_enter = True
if cx.opts.post_return is not None:
cx.opts.post_return(flat_results)
if cx.called_as_export:
cx.inst.may_enter = True

return (results, post_return)

### `lower`

def canon_lower(caller_cx, callee, ft, flat_args):
trap_if(not caller_cx.inst.may_leave)
def canon_lower(cx, callee, ft, flat_args):
trap_if(not cx.inst.may_leave)

flat_args = ValueIter(flat_args)
args = lift_values(caller_cx, MAX_FLAT_PARAMS, flat_args, ft.param_types())
args = lift_values(cx, MAX_FLAT_PARAMS, flat_args, ft.param_types())

results, post_return = callee(args)

caller_cx.inst.may_leave = False
flat_results = lower_values(caller_cx, MAX_FLAT_RESULTS, results, ft.result_types(), flat_args)
caller_cx.inst.may_leave = True
cx.inst.may_leave = False
flat_results = lower_values(cx, MAX_FLAT_RESULTS, results, ft.result_types(), flat_args)
cx.inst.may_leave = True

post_return()

Expand Down
15 changes: 8 additions & 7 deletions design/mvp/canonical-abi/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ def realloc(self, original_ptr, original_size, alignment, new_size):
self.memory[ret : ret + original_size] = self.memory[original_ptr : original_ptr + original_size]
return ret

def mk_cx(memory, encoding = None, realloc = None, post_return = None):
def mk_cx(memory = bytearray(), encoding = 'utf8', realloc = None, post_return = None):
cx = Context()
cx.opts = CanonicalOptions()
cx.opts.memory = memory
cx.opts.string_encoding = encoding
cx.opts.realloc = realloc
cx.opts.post_return = post_return
cx.inst = ComponentInstance()
cx.called_as_export = True
return cx

def mk_str(s):
Expand All @@ -56,7 +57,7 @@ def fail(msg):
raise BaseException(msg)

def test(t, vals_to_lift, v,
cx = mk_cx(bytearray(), 'utf8', None, None),
cx = mk_cx(),
dst_encoding = None,
lower_t = None,
lower_v = None):
Expand Down Expand Up @@ -85,7 +86,7 @@ def test_name():
heap = Heap(5*len(cx.opts.memory))
if dst_encoding is None:
dst_encoding = cx.opts.string_encoding
cx = mk_cx(heap.memory, dst_encoding, heap.realloc, None)
cx = mk_cx(heap.memory, dst_encoding, heap.realloc)
lowered_vals = lower_flat(cx, v, lower_t)
assert(flatten_type(lower_t) == list(map(lambda v: v.t, lowered_vals)))

Expand Down Expand Up @@ -200,7 +201,7 @@ def test_nan64(inbits, outbits):
def test_string_internal(src_encoding, dst_encoding, s, encoded, tagged_code_units):
heap = Heap(len(encoded))
heap.memory[:] = encoded[:]
cx = mk_cx(heap.memory, src_encoding, None, None)
cx = mk_cx(heap.memory, src_encoding)
v = (s, src_encoding, tagged_code_units)
test(String(), [0, tagged_code_units], v, cx, dst_encoding)

Expand Down Expand Up @@ -237,7 +238,7 @@ def test_string(src_encoding, dst_encoding, s):

def test_heap(t, expect, args, byte_array):
heap = Heap(byte_array)
cx = mk_cx(heap.memory, 'utf8', None, None)
cx = mk_cx(heap.memory)
test(t, args, expect, cx)

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

callee_heap = Heap(1000)
callee_cx = mk_cx(callee_heap.memory, 'utf8', callee_heap.realloc, lambda x: () )
lifted_callee = lambda args: canon_lift(callee_cx, callee, ft, args, True)
lifted_callee = lambda args: canon_lift(callee_cx, callee, ft, args)

caller_heap = Heap(1000)
caller_cx = mk_cx(caller_heap.memory, 'utf8', caller_heap.realloc, None)
caller_cx = mk_cx(caller_heap.memory, 'utf8', caller_heap.realloc)

flat_args = lower_flat(caller_cx, v, t)
flat_results = canon_lower(caller_cx, lifted_callee, ft, flat_args)
Expand Down

0 comments on commit e80a1de

Please sign in to comment.