Skip to content

Commit da1cd55

Browse files
authored
Reserve handle index 0 and add handle index max (#284)
Resolves #282
1 parent 9cda856 commit da1cd55

File tree

3 files changed

+34
-25
lines changed

3 files changed

+34
-25
lines changed

design/mvp/CanonicalABI.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ class HandleTable:
350350
free: [int]
351351

352352
def __init__(self):
353-
self.array = []
353+
self.array = [None]
354354
self.free = []
355355

356356
def get(self, i):
@@ -361,7 +361,9 @@ class HandleTable:
361361
The `HandleTable` class maintains a dense array of handles that can contain
362362
holes created by the `remove` method (defined below). When handles are accessed
363363
(by lifting and `resource.rep`), there are thus both a bounds check and hole
364-
check necessary.
364+
check necessary. Upon initialization, table element `0` is allocated and set to
365+
`None`, effectively reserving index `0` which is both useful for catching
366+
null/uninitialized accesses and allowing `0` to serve as a sentinel value.
365367

366368
The `add` and `remove` methods work together to maintain a free list of holes
367369
that are used in preference to growing the table. The free list is represented
@@ -375,6 +377,7 @@ free list in the free elements of `array`.
375377
self.array[i] = h
376378
else:
377379
i = len(self.array)
380+
trap_if(i >= 2**30)
378381
self.array.append(h)
379382
return i
380383

@@ -384,6 +387,9 @@ free list in the free elements of `array`.
384387
self.free.append(i)
385388
return h
386389
```
390+
The handle index limit of `2**20` ensures that the high 2 bits of handle
391+
indices are unset and available for other use in guest code (e.g., for tagging,
392+
packed words or sentinel values).
387393

388394
Finally, we can define `HandleTables` (plural) as simply a wrapper around
389395
a mutable mapping from `ResourceType` to `HandleTable`:

design/mvp/canonical-abi/definitions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ class HandleTable:
336336
free: [int]
337337

338338
def __init__(self):
339-
self.array = []
339+
self.array = [None]
340340
self.free = []
341341

342342
def get(self, i):
@@ -351,6 +351,7 @@ def add(self, h):
351351
self.array[i] = h
352352
else:
353353
i = len(self.array)
354+
trap_if(i >= 2**30)
354355
self.array.append(h)
355356
return i
356357

design/mvp/canonical-abi/run_tests.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -390,13 +390,15 @@ def core_wasm(args):
390390
nonlocal dtor_value
391391

392392
assert(len(args) == 4)
393-
assert(args[0].t == 'i32' and args[0].v == 0)
394-
assert(args[1].t == 'i32' and args[1].v == 1)
395-
assert(args[2].t == 'i32' and args[2].v == 2)
393+
assert(len(inst.handles.table(rt).array) == 4)
394+
assert(inst.handles.table(rt).array[0] is None)
395+
assert(args[0].t == 'i32' and args[0].v == 1)
396+
assert(args[1].t == 'i32' and args[1].v == 2)
397+
assert(args[2].t == 'i32' and args[2].v == 3)
396398
assert(args[3].t == 'i32' and args[3].v == 13)
397-
assert(canon_resource_rep(inst, rt, 0) == 42)
398-
assert(canon_resource_rep(inst, rt, 1) == 43)
399-
assert(canon_resource_rep(inst, rt, 2) == 44)
399+
assert(canon_resource_rep(inst, rt, 1) == 42)
400+
assert(canon_resource_rep(inst, rt, 2) == 43)
401+
assert(canon_resource_rep(inst, rt, 3) == 44)
400402

401403
host_ft = FuncType([
402404
Borrow(rt),
@@ -405,35 +407,35 @@ def core_wasm(args):
405407
Own(rt)
406408
])
407409
args = [
408-
Value('i32',0),
409-
Value('i32',2)
410+
Value('i32',1),
411+
Value('i32',3)
410412
]
411413
results = canon_lower(opts, inst, host_import, True, host_ft, args)
412414
assert(len(results) == 1)
413-
assert(results[0].t == 'i32' and results[0].v == 3)
414-
assert(canon_resource_rep(inst, rt, 3) == 45)
415+
assert(results[0].t == 'i32' and results[0].v == 4)
416+
assert(canon_resource_rep(inst, rt, 4) == 45)
415417

416418
dtor_value = None
417-
canon_resource_drop(inst, rt, 0)
419+
canon_resource_drop(inst, rt, 1)
418420
assert(dtor_value == 42)
419-
assert(len(inst.handles.table(rt).array) == 4)
420-
assert(inst.handles.table(rt).array[0] is None)
421+
assert(len(inst.handles.table(rt).array) == 5)
422+
assert(inst.handles.table(rt).array[1] is None)
421423
assert(len(inst.handles.table(rt).free) == 1)
422424

423425
h = canon_resource_new(inst, rt, 46)
424-
assert(h == 0)
425-
assert(len(inst.handles.table(rt).array) == 4)
426-
assert(inst.handles.table(rt).array[0] is not None)
426+
assert(h == 1)
427+
assert(len(inst.handles.table(rt).array) == 5)
428+
assert(inst.handles.table(rt).array[1] is not None)
427429
assert(len(inst.handles.table(rt).free) == 0)
428430

429431
dtor_value = None
430-
canon_resource_drop(inst, rt, 2)
432+
canon_resource_drop(inst, rt, 3)
431433
assert(dtor_value is None)
432-
assert(len(inst.handles.table(rt).array) == 4)
433-
assert(inst.handles.table(rt).array[2] is None)
434+
assert(len(inst.handles.table(rt).array) == 5)
435+
assert(inst.handles.table(rt).array[3] is None)
434436
assert(len(inst.handles.table(rt).free) == 1)
435437

436-
return [Value('i32', 0), Value('i32', 1), Value('i32', 3)]
438+
return [Value('i32', 1), Value('i32', 2), Value('i32', 4)]
437439

438440
ft = FuncType([
439441
Own(rt),
@@ -457,8 +459,8 @@ def core_wasm(args):
457459
assert(got[0] == 46)
458460
assert(got[1] == 43)
459461
assert(got[2] == 45)
460-
assert(len(inst.handles.table(rt).array) == 4)
461-
assert(all(inst.handles.table(rt).array[i] is None for i in range(3)))
462+
assert(len(inst.handles.table(rt).array) == 5)
463+
assert(all(inst.handles.table(rt).array[i] is None for i in range(4)))
462464
assert(len(inst.handles.table(rt).free) == 4)
463465
definitions.MAX_FLAT_RESULTS = before
464466

0 commit comments

Comments
 (0)