@@ -7,7 +7,7 @@ of modules in Core WebAssembly.
7
7
* [ Supporting definitions] ( #supporting-definitions )
8
8
* [ Despecialization] ( #despecialization )
9
9
* [ Alignment] ( #alignment )
10
- * [ Size] ( #size )
10
+ * [ Element Size] ( #element- size )
11
11
* [ Runtime State] ( #runtime-state )
12
12
* [ Loading] ( #loading )
13
13
* [ Storing] ( #storing )
@@ -156,13 +156,17 @@ Handle types are passed as `i32` indices into the `HandleTable` introduced
156
156
below.
157
157
158
158
159
- ### Size
159
+ ### Element Size
160
160
161
- Each value type is also assigned a ` size ` , measured in bytes, which corresponds
162
- the ` sizeof ` operator in C. Empty types, such as records with no fields, are
163
- not permitted, to avoid complications in source languages.
161
+ Each value type is also assigned an ` elem_size ` which is the number of bytes
162
+ used when values of the type are stored as elements of a ` list ` . Having this
163
+ byte size be a static property of the type instead of attempting to use a
164
+ variable-length element-encoding scheme both simplifies the implementation and
165
+ maps well to languages which represent ` list ` s as random-access arrays. Empty
166
+ types, such as records with no fields, are not permitted, to avoid
167
+ complications in source languages.
164
168
``` python
165
- def size (t ):
169
+ def elem_size (t ):
166
170
match despecialize(t):
167
171
case Bool() : return 1
168
172
case S8() | U8() : return 1
@@ -173,33 +177,33 @@ def size(t):
173
177
case F64() : return 8
174
178
case Char() : return 4
175
179
case String() | List(_) : return 8
176
- case Record(fields) : return size_record (fields)
177
- case Variant(cases) : return size_variant (cases)
178
- case Flags(labels) : return size_flags (labels)
180
+ case Record(fields) : return elem_size_record (fields)
181
+ case Variant(cases) : return elem_size_variant (cases)
182
+ case Flags(labels) : return elem_size_flags (labels)
179
183
case Own(_) | Borrow(_) : return 4
180
184
181
- def size_record (fields ):
185
+ def elem_size_record (fields ):
182
186
s = 0
183
187
for f in fields:
184
188
s = align_to(s, alignment(f.t))
185
- s += size (f.t)
189
+ s += elem_size (f.t)
186
190
assert (s > 0 )
187
191
return align_to(s, alignment_record(fields))
188
192
189
193
def align_to (ptr , alignment ):
190
194
return math.ceil(ptr / alignment) * alignment
191
195
192
- def size_variant (cases ):
193
- s = size (discriminant_type(cases))
196
+ def elem_size_variant (cases ):
197
+ s = elem_size (discriminant_type(cases))
194
198
s = align_to(s, max_case_alignment(cases))
195
199
cs = 0
196
200
for c in cases:
197
201
if c.t is not None :
198
- cs = max (cs, size (c.t))
202
+ cs = max (cs, elem_size (c.t))
199
203
s += cs
200
204
return align_to(s, alignment_variant(cases))
201
205
202
- def size_flags (labels ):
206
+ def elem_size_flags (labels ):
203
207
n = len (labels)
204
208
assert (n > 0 )
205
209
if n <= 8 : return 1
@@ -430,7 +434,7 @@ the top-level case analysis:
430
434
``` python
431
435
def load (cx , ptr , t ):
432
436
assert (ptr == align_to(ptr, alignment(t)))
433
- assert (ptr + size (t) <= len (cx.opts.memory))
437
+ assert (ptr + elem_size (t) <= len (cx.opts.memory))
434
438
match despecialize(t):
435
439
case Bool() : return convert_int_to_bool(load_int(cx, ptr, 1 ))
436
440
case U8() : return load_int(cx, ptr, 1 )
@@ -511,6 +515,7 @@ testing that its unsigned integral value is in the valid [Unicode Code Point]
511
515
range and not a [ Surrogate] :
512
516
``` python
513
517
def convert_i32_to_char (cx , i ):
518
+ assert (i >= 0 )
514
519
trap_if(i >= 0x 110000 )
515
520
trap_if(0x D800 <= i <= 0x DFFF )
516
521
return chr (i)
@@ -571,18 +576,18 @@ def load_list(cx, ptr, elem_type):
571
576
572
577
def load_list_from_range (cx , ptr , length , elem_type ):
573
578
trap_if(ptr != align_to(ptr, alignment(elem_type)))
574
- trap_if(ptr + length * size (elem_type) > len (cx.opts.memory))
579
+ trap_if(ptr + length * elem_size (elem_type) > len (cx.opts.memory))
575
580
a = []
576
581
for i in range (length):
577
- a.append(load(cx, ptr + i * size (elem_type), elem_type))
582
+ a.append(load(cx, ptr + i * elem_size (elem_type), elem_type))
578
583
return a
579
584
580
585
def load_record (cx , ptr , fields ):
581
586
record = {}
582
587
for field in fields:
583
588
ptr = align_to(ptr, alignment(field.t))
584
589
record[field.label] = load(cx, ptr, field.t)
585
- ptr += size (field.t)
590
+ ptr += elem_size (field.t)
586
591
return record
587
592
```
588
593
As a technical detail: the ` align_to ` in the loop in ` load_record ` is
@@ -599,7 +604,7 @@ implementation can build the appropriate index tables at compile-time so that
599
604
variant-passing is always O(1) and not involving string operations.
600
605
``` python
601
606
def load_variant (cx , ptr , cases ):
602
- disc_size = size (discriminant_type(cases))
607
+ disc_size = elem_size (discriminant_type(cases))
603
608
case_index = load_int(cx, ptr, disc_size)
604
609
ptr += disc_size
605
610
trap_if(case_index >= len (cases))
@@ -630,7 +635,7 @@ derived from the ordered labels of the `flags` type. The code here takes
630
635
advantage of Python's support for integers of arbitrary width.
631
636
``` python
632
637
def load_flags (cx , ptr , labels ):
633
- i = load_int(cx, ptr, size_flags (labels))
638
+ i = load_int(cx, ptr, elem_size_flags (labels))
634
639
return unpack_flags_from_int(i, labels)
635
640
636
641
def unpack_flags_from_int (i , labels ):
@@ -670,8 +675,13 @@ def lift_borrow(cx, i, t):
670
675
cx.track_owning_lend(h)
671
676
return h.rep
672
677
```
673
- The ` track_owning_lend ` call to ` CallContext ` participates in the enforcement of
674
- the dynamic borrow rules.
678
+ The ` track_owning_lend ` call to ` CallContext ` participates in the enforcement
679
+ of the dynamic borrow rules, which keep the source ` own ` handle alive until the
680
+ end of the call (as an intentionally-conservative upper bound on how long the
681
+ ` borrow ` handle can be held). This tracking is only required when ` h ` is an
682
+ ` own ` handle because, when ` h ` is a ` borrow ` handle, this tracking has already
683
+ happened (when the originating ` own ` handle was lifted) for a strictly longer
684
+ call scope than the current call.
675
685
676
686
677
687
### Storing
@@ -682,7 +692,7 @@ The `store` function defines how to write a value `v` of a given value type
682
692
``` python
683
693
def store (cx , v , t , ptr ):
684
694
assert (ptr == align_to(ptr, alignment(t)))
685
- assert (ptr + size (t) <= len (cx.opts.memory))
695
+ assert (ptr + elem_size (t) <= len (cx.opts.memory))
686
696
match despecialize(t):
687
697
case Bool() : store_int(cx, int (bool (v)), ptr, 1 )
688
698
case U8() : store_int(cx, v, ptr, 1 )
@@ -990,20 +1000,20 @@ def store_list(cx, v, ptr, elem_type):
990
1000
store_int(cx, length, ptr + 4 , 4 )
991
1001
992
1002
def store_list_into_range (cx , v , elem_type ):
993
- byte_length = len (v) * size (elem_type)
1003
+ byte_length = len (v) * elem_size (elem_type)
994
1004
trap_if(byte_length >= (1 << 32 ))
995
1005
ptr = cx.opts.realloc(0 , 0 , alignment(elem_type), byte_length)
996
1006
trap_if(ptr != align_to(ptr, alignment(elem_type)))
997
1007
trap_if(ptr + byte_length > len (cx.opts.memory))
998
1008
for i,e in enumerate (v):
999
- store(cx, e, elem_type, ptr + i * size (elem_type))
1009
+ store(cx, e, elem_type, ptr + i * elem_size (elem_type))
1000
1010
return (ptr, len (v))
1001
1011
1002
1012
def store_record (cx , v , ptr , fields ):
1003
1013
for f in fields:
1004
1014
ptr = align_to(ptr, alignment(f.t))
1005
1015
store(cx, v[f.label], f.t, ptr)
1006
- ptr += size (f.t)
1016
+ ptr += elem_size (f.t)
1007
1017
```
1008
1018
1009
1019
Variants are stored using the ` | ` -separated list of ` refines ` cases built
@@ -1015,7 +1025,7 @@ case indices to the consumer's case indices.
1015
1025
``` python
1016
1026
def store_variant (cx , v , ptr , cases ):
1017
1027
case_index, case_value = match_case(v, cases)
1018
- disc_size = size (discriminant_type(cases))
1028
+ disc_size = elem_size (discriminant_type(cases))
1019
1029
store_int(cx, case_index, ptr, disc_size)
1020
1030
ptr += disc_size
1021
1031
ptr = align_to(ptr, max_case_alignment(cases))
@@ -1042,7 +1052,7 @@ to variants.
1042
1052
``` python
1043
1053
def store_flags (cx , v , ptr , labels ):
1044
1054
i = pack_flags_into_int(v, labels)
1045
- store_int(cx, i, ptr, size_flags (labels))
1055
+ store_int(cx, i, ptr, elem_size_flags (labels))
1046
1056
1047
1057
def pack_flags_into_int (v , labels ):
1048
1058
i = 0
@@ -1292,7 +1302,7 @@ def lift_flat_variant(cx, vi, cases):
1292
1302
case (' i64' , ' i32' ) : return wrap_i64_to_i32(x)
1293
1303
case (' i64' , ' f32' ) : return decode_i32_as_float(wrap_i64_to_i32(x))
1294
1304
case (' i64' , ' f64' ) : return decode_i64_as_float(x)
1295
- case _ : return x
1305
+ case _ : assert (have == want); return x
1296
1306
c = cases[case_index]
1297
1307
if c.t is None :
1298
1308
v = None
@@ -1403,7 +1413,7 @@ def lower_flat_variant(cx, v, cases):
1403
1413
case (' i32' , ' i64' ) : payload[i] = Value(' i64' , have.v)
1404
1414
case (' f32' , ' i64' ) : payload[i] = Value(' i64' , encode_float_as_i32(have.v))
1405
1415
case (' f64' , ' i64' ) : payload[i] = Value(' i64' , encode_float_as_i64(have.v))
1406
- case _ : pass
1416
+ case _ : assert (have.t == want)
1407
1417
for want in flat_types:
1408
1418
payload.append(Value(want, 0 ))
1409
1419
return [Value(' i32' , case_index)] + payload
@@ -1433,7 +1443,7 @@ def lift_values(cx, max_flat, vi, ts):
1433
1443
ptr = vi.next(' i32' )
1434
1444
tuple_type = Tuple(ts)
1435
1445
trap_if(ptr != align_to(ptr, alignment(tuple_type)))
1436
- trap_if(ptr + size (tuple_type) > len (cx.opts.memory))
1446
+ trap_if(ptr + elem_size (tuple_type) > len (cx.opts.memory))
1437
1447
return list (load(cx, ptr, tuple_type).values())
1438
1448
else :
1439
1449
return [ lift_flat(cx, vi, t) for t in ts ]
@@ -1451,11 +1461,11 @@ def lower_values(cx, max_flat, vs, ts, out_param = None):
1451
1461
tuple_type = Tuple(ts)
1452
1462
tuple_value = {str (i): v for i,v in enumerate (vs)}
1453
1463
if out_param is None :
1454
- ptr = cx.opts.realloc(0 , 0 , alignment(tuple_type), size (tuple_type))
1464
+ ptr = cx.opts.realloc(0 , 0 , alignment(tuple_type), elem_size (tuple_type))
1455
1465
else :
1456
1466
ptr = out_param.next(' i32' )
1457
1467
trap_if(ptr != align_to(ptr, alignment(tuple_type)))
1458
- trap_if(ptr + size (tuple_type) > len (cx.opts.memory))
1468
+ trap_if(ptr + elem_size (tuple_type) > len (cx.opts.memory))
1459
1469
store(cx, tuple_value, tuple_type, ptr)
1460
1470
return [ Value(' i32' , ptr) ]
1461
1471
else :
0 commit comments