@@ -219,7 +219,6 @@ class Context:
219
219
opts: CanonicalOptions
220
220
inst: ComponentInstance
221
221
call: Call
222
- called_as_export: bool
223
222
```
224
223
225
224
The ` opts ` field represents the [ ` canonopt ` ] values supplied to
@@ -250,20 +249,17 @@ class ComponentInstance:
250
249
self .handles = HandleTable()
251
250
```
252
251
253
- Lastly, the ` called_as_export ` field of ` Context ` indicates whether the lifted
254
- function is being called through a component export or whether this is an
255
- internal call (for example, when a child component calls an import that is
256
- defined by its parent component).
257
-
258
252
The ` HandleTable ` class is defined in terms of a collection of supporting
259
253
runtime bookkeeping classes that we'll go through first.
260
254
261
255
The ` Resource ` class represents a runtime instance of a resource type, storing
262
- the core representation value (which is currently fixed to ` i32 ` ):
256
+ the core representation value (which is currently fixed to ` i32 ` ) and the
257
+ component instance that is implementing this resource.
263
258
``` python
264
259
@dataclass
265
260
class Resource :
266
261
rep: int
262
+ impl: ComponentInstance
267
263
```
268
264
269
265
The ` OwnHandle ` and ` BorrowHandle ` classes represent runtime handle values of
@@ -1422,11 +1418,7 @@ component*.
1422
1418
Given the above closure arguments, ` canon_lift ` is defined:
1423
1419
``` python
1424
1420
def canon_lift (cx , callee , ft , call , args ):
1425
- if cx.called_as_export:
1426
- trap_if(not cx.inst.may_enter)
1427
- cx.inst.may_enter = False
1428
- else :
1429
- assert (not cx.inst.may_enter)
1421
+ trap_if(not cx.inst.may_enter)
1430
1422
1431
1423
outer_call = cx.call
1432
1424
cx.call = call
@@ -1447,9 +1439,6 @@ def canon_lift(cx, callee, ft, call, args):
1447
1439
if cx.opts.post_return is not None :
1448
1440
cx.opts.post_return(flat_results)
1449
1441
1450
- if cx.called_as_export:
1451
- cx.inst.may_enter = True
1452
-
1453
1442
cx.call.finish_lift()
1454
1443
cx.call = outer_call
1455
1444
@@ -1462,14 +1451,6 @@ boundaries. Thus, if a component wishes to signal an error, it must use some
1462
1451
sort of explicit type such as ` result ` (whose ` error ` case particular language
1463
1452
bindings may choose to map to and from exceptions).
1464
1453
1465
- By clearing ` may_enter ` for the duration of ` canon_lift ` when the function is
1466
- called as an export, the dynamic traps ensure that components cannot be
1467
- reentered, ensuring the non-reentrance [ component invariant] . Furthermore,
1468
- because ` may_enter ` is not cleared on the exceptional exit path taken by
1469
- ` trap() ` , if there is a trap during Core WebAssembly execution of lifting or
1470
- lowering, the component is left permanently un-enterable, ensuring the
1471
- lockdown-after-trap [ component invariant] .
1472
-
1473
1454
The ` call ` parameter is assumed to have been created by the caller (the host or
1474
1455
` canon lower ` ) for this one call. Since, in ` not cx.called_as_export ` scenarios
1475
1456
a single component instance may be reentered (by its children), ` cx.call ` must
@@ -1500,9 +1481,13 @@ Thus, from the perspective of Core WebAssembly, `$f` is a [function instance]
1500
1481
containing a ` hostfunc ` that closes over ` $opts ` , ` $inst ` , ` $callee ` and ` $ft `
1501
1482
and, when called from Core WebAssembly code, calls ` canon_lower ` , which is defined as:
1502
1483
``` python
1503
- def canon_lower (cx , callee , ft , flat_args ):
1484
+ def canon_lower (cx , callee , calling_import , ft , flat_args ):
1504
1485
trap_if(not cx.inst.may_leave)
1505
1486
1487
+ assert (cx.inst.may_enter)
1488
+ if calling_import:
1489
+ cx.inst.may_enter = False
1490
+
1506
1491
outer_call = cx.call
1507
1492
cx.call = Call()
1508
1493
@@ -1520,6 +1505,9 @@ def canon_lower(cx, callee, ft, flat_args):
1520
1505
cx.call.finish_lower()
1521
1506
cx.call = outer_call
1522
1507
1508
+ if calling_import:
1509
+ cx.inst.may_enter = True
1510
+
1523
1511
return flat_results
1524
1512
```
1525
1513
The definitions of ` canon_lift ` and ` canon_lower ` are mostly symmetric (swapping
@@ -1538,6 +1526,20 @@ compilation of the permissive [subtyping](Subtyping.md) allowed between
1538
1526
components (including the elimination of string operations on the labels of
1539
1527
records and variants) as well as post-MVP [ adapter functions] .
1540
1528
1529
+ By clearing ` may_enter ` for the duration of calls to imports, the ` may_enter `
1530
+ guard in ` canon_lift ` ensures that components cannot be externally reentered,
1531
+ which is part of the [ component invariants] . The ` calling_import ` condition
1532
+ allows a parent component to call into a child component (which is, by
1533
+ definition, not a call to an import) and for the child to then reenter the
1534
+ parent through a function the parent explicitly supplied to the child's
1535
+ ` instantiate ` . This form of internal reentrance allows the parent to fully
1536
+ virtualize the child's imports.
1537
+
1538
+ Because ` may_enter ` is not cleared on the exceptional exit path taken by
1539
+ ` trap() ` , if there is a trap during Core WebAssembly execution of lifting or
1540
+ lowering, the component is left permanently un-enterable, ensuring the
1541
+ lockdown-after-trap [ component invariant] .
1542
+
1541
1543
The ` may_leave ` flag set during lowering in ` canon_lift ` and ` canon_lower `
1542
1544
ensures that the relative ordering of the side effects of ` lift ` and ` lower `
1543
1545
cannot be observed via import calls and thus an implementation may reliably
@@ -1567,7 +1569,7 @@ Calling `$f` invokes the following function, which creates a resource object
1567
1569
and inserts it into the current instance's handle table:
1568
1570
``` python
1569
1571
def canon_resource_new (cx , rt , rep ):
1570
- h = OwnHandle(Resource(rep), rt)
1572
+ h = OwnHandle(Resource(rep, cx.inst ), rt)
1571
1573
return cx.inst.handles.insert(cx, h)
1572
1574
```
1573
1575
@@ -1588,8 +1590,11 @@ optional destructor.
1588
1590
def canon_resource_drop (cx , t , i ):
1589
1591
h = cx.inst.handles.remove(cx, i, t)
1590
1592
if isinstance (t, Own) and t.rt.dtor:
1593
+ trap_if(not h.resource.impl.may_enter)
1591
1594
t.rt.dtor(h.resource.rep)
1592
1595
```
1596
+ The ` may_enter ` guard ensures the non-reentrance [ component invariant] , since
1597
+ a destructor call is analogous to a call to an export.
1593
1598
1594
1599
### ` canon resource.rep `
1595
1600
@@ -1618,6 +1623,7 @@ def canon_resource_rep(cx, rt, i):
1618
1623
[ `canonopt` ] : Explainer.md#canonical-definitions
1619
1624
[ `canon` ] : Explainer.md#canonical-definitions
1620
1625
[ Type Definitions ] : Explainer.md#type-definitions
1626
+ [ Component Invariant ] : Explainer.md#component-invariants
1621
1627
[ Component Invariants ] : Explainer.md#component-invariants
1622
1628
[ JavaScript Embedding ] : Explainer.md#JavaScript-embedding
1623
1629
[ Adapter Functions ] : FutureFeatures.md#custom-abis-via-adapter-functions
0 commit comments