@@ -241,21 +241,20 @@ canonical definition is defined to execute inside. The `may_enter` and
241
241
` may_leave ` fields are used to enforce the [ component invariants] : ` may_leave `
242
242
indicates whether the instance may call out to an import and the ` may_enter `
243
243
state indicates whether the instance may be called from the outside world
244
- through an export. The ` HandleTable ` is defined next.
244
+ through an export.
245
245
``` python
246
246
class ComponentInstance :
247
247
may_leave: bool
248
248
may_enter: bool
249
- handles: HandleTable
249
+ handles: HandleTables
250
250
251
251
def __init__ (self ):
252
252
self .may_leave = True
253
253
self .may_enter = True
254
- self .handles = HandleTable ()
254
+ self .handles = HandleTables ()
255
255
```
256
-
257
- The ` HandleTable ` class is defined in terms of a collection of supporting
258
- runtime bookkeeping classes that we'll go through first.
256
+ ` HandleTables ` is defined in terms of a collection of supporting runtime
257
+ bookkeeping classes that we'll go through first.
259
258
260
259
The ` ResourceType ` class represents a resource type that has been defined by
261
260
the specific component instance pointed to by ` impl ` with a particular
@@ -275,7 +274,6 @@ handle refers to.
275
274
@dataclass
276
275
class Handle :
277
276
rep: int
278
- rt: ResourceType
279
277
lend_count: int
280
278
281
279
@dataclass
@@ -286,13 +284,10 @@ class OwnHandle(Handle):
286
284
class BorrowHandle (Handle ):
287
285
scope: BorrowScope
288
286
```
289
- The ` rt ` field points to a runtime value representing the static
290
- [ ` resourcetype ` ] ( Explainer.md#type-definitions ) of this handle and is used by
291
- dynamic type checking below. Lastly, the ` lend_count ` field maintains a count
292
- of the outstanding handles that were lent from this handle (by calls to
293
- ` borrow ` -taking functions). This count is used below to dynamically enforce the
294
- invariant that a handle cannot be dropped while it has currently lent out a
295
- ` borrow ` .
287
+ The ` lend_count ` field maintains a count of the outstanding handles that were
288
+ lent from this handle (by calls to ` borrow ` -taking functions). This count is
289
+ used below to dynamically enforce the invariant that a handle cannot be
290
+ dropped while it has currently lent out a ` borrow ` .
296
291
297
292
The ` BorrowScope ` class represents the scope of a single runtime call of a
298
293
component-level function during which zero or more handles are borrowed.
@@ -326,8 +321,9 @@ lent out a `BorrowHandle` and are to be restored when the call finishes and
326
321
stored inline in the stack frame with a layout specialized to the function
327
322
signature, thereby avoiding dynamic allocation of ` lenders ` in many cases.
328
323
329
- Based on these supporting runtime data structures, we can define the
330
- ` HandleTable ` in pieces, starting with its fields and the ` insert ` method:
324
+ ` HandleTable ` (singular) encapsulates a single mutable, growable array
325
+ of handles that all share the same ` ResourceType ` . Defining ` HandleTable ` in
326
+ chunks, we start with the fields and ` insert ` method:
331
327
``` python
332
328
class HandleTable :
333
329
array: [Optional[Handle]]
@@ -358,26 +354,18 @@ The `get` method is used by other `HandleTable` methods and canonical
358
354
definitions below and uses dynamic guards to catch out-of-bounds and
359
355
use-after-free:
360
356
``` python
361
- def get (self , i , rt ):
357
+ def get (self , i ):
362
358
trap_if(i >= len (self .array))
363
359
trap_if(self .array[i] is None )
364
- trap_if(self .array[i].rt is not rt)
365
360
return self .array[i]
366
361
```
367
- Additionally, the ` get ` method takes the runtime resource type tag and checks
368
- a tag match before returning the handle with this new-valid resource type. This
369
- check is a non-structural, pointer-equality-based test used to enforce the
370
- [ type-checking rules] ( Explainer.md ) of resource types at runtime. Importantly,
371
- this check keeps type imports abstract, considering each type import to have a
372
- unique ` rt ` value distinct from every other type import even if the two imports
373
- happen to be instantiated with the same resource type at runtime.
374
-
375
- Finally, the ` remove ` method is used to drop or transfer a handle out of the
376
- handle table. ` remove ` adds the removed handle to the ` free ` list for later
377
- recycling by ` insert ` (above).
362
+
363
+ The last method of ` HandleTable ` , ` remove ` , is used to drop or transfer a
364
+ handle out of the handle table. ` remove ` adds the removed handle to the ` free `
365
+ list for later recycling by ` insert ` (above).
378
366
``` python
379
367
def remove (self , i , t ):
380
- h = self .get(i, t.rt )
368
+ h = self .get(i)
381
369
trap_if(h.lend_count != 0 )
382
370
match t:
383
371
case Own(_):
@@ -398,6 +386,36 @@ previous owner. The bookkeeping performed by `remove` for borrowed handles
398
386
records the fulfillment of the obligation of the borrower to drop the handle
399
387
before the end of the call.
400
388
389
+ Finally, we can define ` HandleTables ` (plural) as simply a wrapper around
390
+ a mutable mapping from ` ResourceType ` to ` HandleTable ` :
391
+ ``` python
392
+ class HandleTables :
393
+ rt_to_table: MutableMapping[ResourceType, HandleTable]
394
+
395
+ def __init__ (self ):
396
+ self .rt_to_table = dict ()
397
+
398
+ def table (self , rt ):
399
+ if id (rt) not in self .rt_to_table:
400
+ self .rt_to_table[id (rt)] = HandleTable()
401
+ return self .rt_to_table[id (rt)]
402
+
403
+ def insert (self , h , rt ):
404
+ return self .table(rt).insert(h)
405
+ def get (self , i , rt ):
406
+ return self .table(rt).get(i)
407
+ def remove (self , i , t ):
408
+ return self .table(t.rt).remove(i, t)
409
+ ```
410
+ While this Python code performs a dynamic hash-table lookup on each handle
411
+ table access, as we'll see below, the ` rt ` parameter is always statically
412
+ known such that a normal implementation can statically enumerate all
413
+ ` HandleTable ` objects at compile time and then route the calls to ` insert ` ,
414
+ ` get ` and ` remove ` to the correct ` HandleTable ` at the callsite. The net
415
+ result is that each component instance will contain one handle table per
416
+ resource type used by the component, with each compiled adapter function
417
+ accessing the correct handle table as-if it were a global variable.
418
+
401
419
402
420
### Loading
403
421
@@ -963,16 +981,16 @@ current component instance's `HandleTable`:
963
981
``` python
964
982
def lower_own (cx , src , rt ):
965
983
assert (isinstance (src, OwnHandle))
966
- h = OwnHandle(src.rep, rt, 0 )
967
- return cx.inst.handles.insert(h)
984
+ h = OwnHandle(src.rep, 0 )
985
+ return cx.inst.handles.insert(h, rt )
968
986
969
987
def lower_borrow (cx , src , rt ):
970
988
assert (isinstance (src, Handle))
971
989
if cx.inst is rt.impl:
972
990
return src.rep
973
991
cx.borrow_scope.add(src)
974
- h = BorrowHandle(src.rep, rt, 0 , cx.borrow_scope)
975
- return cx.inst.handles.insert(h)
992
+ h = BorrowHandle(src.rep, 0 , cx.borrow_scope)
993
+ return cx.inst.handles.insert(h, rt )
976
994
```
977
995
The special case in ` lower_borrow ` is an optimization, recognizing that, when
978
996
a borrowed handle is passed to the component that implemented the resource
@@ -1551,8 +1569,8 @@ Calling `$f` invokes the following function, which creates a resource object
1551
1569
and inserts it into the current instance's handle table:
1552
1570
``` python
1553
1571
def canon_resource_new (inst , rt , rep ):
1554
- h = OwnHandle(rep, rt, 0 )
1555
- return inst.handles.insert(h)
1572
+ h = OwnHandle(rep, 0 )
1573
+ return inst.handles.insert(h, rt )
1556
1574
```
1557
1575
1558
1576
### ` canon resource.drop `
0 commit comments