@@ -68,13 +68,12 @@ function to replace specialized value types with their expansion:
68
68
``` python
69
69
def despecialize (t ):
70
70
match t:
71
- case Tuple(ts) : return Record([ Field(str (i), t) for i,t in enumerate (ts) ])
72
- case Unit() : return Record([])
73
- case Union(ts) : return Variant([ Case(str (i), t) for i,t in enumerate (ts) ])
74
- case Enum(labels) : return Variant([ Case(l, Unit()) for l in labels ])
75
- case Option(t) : return Variant([ Case(" none" , Unit()), Case(" some" , t) ])
76
- case Expected(ok, error) : return Variant([ Case(" ok" , ok), Case(" error" , error) ])
77
- case _ : return t
71
+ case Tuple(ts) : return Record([ Field(str (i), t) for i,t in enumerate (ts) ])
72
+ case Union(ts) : return Variant([ Case(str (i), t) for i,t in enumerate (ts) ])
73
+ case Enum(labels) : return Variant([ Case(l, None ) for l in labels ])
74
+ case Option(t) : return Variant([ Case(" none" , None ), Case(" some" , t) ])
75
+ case Result(ok, error) : return Variant([ Case(" ok" , ok), Case(" error" , error) ])
76
+ case _ : return t
78
77
```
79
78
The specialized value types ` string ` and ` flags ` are missing from this list
80
79
because they are given specialized canonical ABI representations distinct from
@@ -98,17 +97,17 @@ def alignment(t):
98
97
case Float64() : return 8
99
98
case Char() : return 4
100
99
case String() | List(_) : return 4
101
- case Record(fields) : return max_alignment(types_of( fields) )
102
- case Variant(cases) : return max_alignment(types_of( cases) + [discriminant_type(cases)] )
100
+ case Record(fields) : return alignment_record( fields)
101
+ case Variant(cases) : return alignment_variant( cases)
103
102
case Flags(labels) : return alignment_flags(labels)
103
+ ```
104
104
105
- def types_of (fields_or_cases ):
106
- return [x.t for x in fields_or_cases]
107
-
108
- def max_alignment (ts ):
105
+ Record alignment is tuple alignment, with the definitions split for reuse below:
106
+ ``` python
107
+ def alignment_record (fields ):
109
108
a = 1
110
- for t in ts :
111
- a = max (a, alignment(t))
109
+ for f in fields :
110
+ a = max (a, alignment(f. t))
112
111
return a
113
112
```
114
113
@@ -117,6 +116,9 @@ covering the number of cases in the variant. Depending on the payload type,
117
116
this can allow more compact representations of variants in memory. This smallest
118
117
integer type is selected by the following function, used above and below:
119
118
``` python
119
+ def alignment_variant (cases ):
120
+ return max (alignment(discriminant_type(cases)), max_case_alignment(cases))
121
+
120
122
def discriminant_type (cases ):
121
123
n = len (cases)
122
124
assert (0 < n < (1 << 32 ))
@@ -125,6 +127,13 @@ def discriminant_type(cases):
125
127
case 1 : return U8()
126
128
case 2 : return U16()
127
129
case 3 : return U32()
130
+
131
+ def max_case_alignment (cases ):
132
+ a = 1
133
+ for c in cases:
134
+ if c.t is not None :
135
+ a = max (a, alignment(c.t))
136
+ return a
128
137
```
129
138
130
139
As an optimization, ` flags ` are represented as packed bit-vectors. Like variant
@@ -164,19 +173,20 @@ def size_record(fields):
164
173
for f in fields:
165
174
s = align_to(s, alignment(f.t))
166
175
s += size(f.t)
167
- return align_to(s, alignment(Record( fields) ))
176
+ return align_to(s, alignment_record( fields))
168
177
169
178
def align_to (ptr , alignment ):
170
179
return math.ceil(ptr / alignment) * alignment
171
180
172
181
def size_variant (cases ):
173
182
s = size(discriminant_type(cases))
174
- s = align_to(s, max_alignment(types_of( cases) ))
183
+ s = align_to(s, max_case_alignment( cases))
175
184
cs = 0
176
185
for c in cases:
177
- cs = max (cs, size(c.t))
186
+ if c.t is not None :
187
+ cs = max (cs, size(c.t))
178
188
s += cs
179
- return align_to(s, alignment(Variant( cases) ))
189
+ return align_to(s, alignment_variant( cases))
180
190
181
191
def size_flags (labels ):
182
192
n = len (labels)
@@ -362,18 +372,21 @@ string operations.
362
372
``` python
363
373
def load_variant (opts , ptr , cases ):
364
374
disc_size = size(discriminant_type(cases))
365
- disc = load_int(opts, ptr, disc_size)
375
+ case_index = load_int(opts, ptr, disc_size)
366
376
ptr += disc_size
367
- trap_if(disc >= len (cases))
368
- case = cases[disc]
369
- ptr = align_to(ptr, max_alignment(types_of(cases)))
370
- return { case_label_with_refinements(case, cases): load(opts, ptr, case.t) }
371
-
372
- def case_label_with_refinements (case , cases ):
373
- label = case.label
374
- while case.refines is not None :
375
- case = cases[find_case(case.refines, cases)]
376
- label += ' |' + case.label
377
+ trap_if(case_index >= len (cases))
378
+ c = cases[case_index]
379
+ ptr = align_to(ptr, max_case_alignment(cases))
380
+ case_label = case_label_with_refinements(c, cases)
381
+ if c.t is None :
382
+ return { case_label: None }
383
+ return { case_label: load(opts, ptr, c.t) }
384
+
385
+ def case_label_with_refinements (c , cases ):
386
+ label = c.label
387
+ while c.refines is not None :
388
+ c = cases[find_case(c.refines, cases)]
389
+ label += ' |' + c.label
377
390
return label
378
391
379
392
def find_case (label , cases ):
@@ -702,8 +715,10 @@ def store_variant(opts, v, ptr, cases):
702
715
disc_size = size(discriminant_type(cases))
703
716
store_int(opts, case_index, ptr, disc_size)
704
717
ptr += disc_size
705
- ptr = align_to(ptr, max_alignment(types_of(cases)))
706
- store(opts, case_value, cases[case_index].t, ptr)
718
+ ptr = align_to(ptr, max_case_alignment(cases))
719
+ c = cases[case_index]
720
+ if c.t is not None :
721
+ store(opts, case_value, c.t, ptr)
707
722
708
723
def match_case (v , cases ):
709
724
assert (len (v.keys()) == 1 )
@@ -771,7 +786,7 @@ def flatten(functype, context):
771
786
if len (flat_params) > MAX_FLAT_PARAMS :
772
787
flat_params = [' i32' ]
773
788
774
- flat_results = flatten_type (functype.result )
789
+ flat_results = flatten_types (functype.results )
775
790
if len (flat_results) > MAX_FLAT_RESULTS :
776
791
match context:
777
792
case ' lift' :
@@ -799,11 +814,20 @@ def flatten_type(t):
799
814
case Float64() : return [' f64' ]
800
815
case Char() : return [' i32' ]
801
816
case String() | List(_) : return [' i32' , ' i32' ]
802
- case Record(fields) : return flatten_types(types_of( fields) )
817
+ case Record(fields) : return flatten_record( fields)
803
818
case Variant(cases) : return flatten_variant(cases)
804
819
case Flags(labels) : return [' i32' ] * num_i32_flags(labels)
805
820
```
806
821
822
+ Record flattening simply flattens each field in sequence.
823
+ ``` python
824
+ def flatten_record (fields ):
825
+ flat = []
826
+ for f in fields:
827
+ flat += flatten_type(f.t)
828
+ return flat
829
+ ```
830
+
807
831
Variant flattening is more involved due to the fact that each case payload can
808
832
have a totally different flattening. Rather than giving up when there is a type
809
833
mismatch, the Canonical ABI relies on the fact that the 4 core value types can
@@ -816,11 +840,12 @@ an `i32` into an `i64`.
816
840
def flatten_variant (cases ):
817
841
flat = []
818
842
for c in cases:
819
- for i,ft in enumerate (flatten_type(c.t)):
820
- if i < len (flat):
821
- flat[i] = join(flat[i], ft)
822
- else :
823
- flat.append(ft)
843
+ if c.t is not None :
844
+ for i,ft in enumerate (flatten_type(c.t)):
845
+ if i < len (flat):
846
+ flat[i] = join(flat[i], ft)
847
+ else :
848
+ flat.append(ft)
824
849
return flatten_type(discriminant_type(cases)) + flat
825
850
826
851
def join (a , b ):
@@ -929,9 +954,8 @@ high bits of an `i64` are set for a 32-bit type:
929
954
def lift_flat_variant (opts , vi , cases ):
930
955
flat_types = flatten_variant(cases)
931
956
assert (flat_types.pop(0 ) == ' i32' )
932
- disc = vi.next(' i32' )
933
- trap_if(disc >= len (cases))
934
- case = cases[disc]
957
+ case_index = vi.next(' i32' )
958
+ trap_if(case_index >= len (cases))
935
959
class CoerceValueIter :
936
960
def next (self , want ):
937
961
have = flat_types.pop(0 )
@@ -942,10 +966,14 @@ def lift_flat_variant(opts, vi, cases):
942
966
case (' i64' , ' f32' ) : return reinterpret_i32_as_float(wrap_i64_to_i32(x))
943
967
case (' i64' , ' f64' ) : return reinterpret_i64_as_float(x)
944
968
case _ : return x
945
- v = lift_flat(opts, CoerceValueIter(), case.t)
969
+ c = cases[case_index]
970
+ if c.t is None :
971
+ v = None
972
+ else :
973
+ v = lift_flat(opts, CoerceValueIter(), c.t)
946
974
for have in flat_types:
947
975
_ = vi.next(have)
948
- return { case_label_with_refinements(case , cases): v }
976
+ return { case_label_with_refinements(c , cases): v }
949
977
950
978
def wrap_i64_to_i32 (i ):
951
979
assert (0 <= i < (1 << 64 ))
@@ -1034,7 +1062,11 @@ def lower_flat_variant(opts, v, cases):
1034
1062
case_index, case_value = match_case(v, cases)
1035
1063
flat_types = flatten_variant(cases)
1036
1064
assert (flat_types.pop(0 ) == ' i32' )
1037
- payload = lower_flat(opts, case_value, cases[case_index].t)
1065
+ c = cases[case_index]
1066
+ if c.t is None :
1067
+ payload = []
1068
+ else :
1069
+ payload = lower_flat(opts, case_value, c.t)
1038
1070
for i,have in enumerate (payload):
1039
1071
want = flat_types.pop(0 )
1040
1072
match (have.t, want):
@@ -1177,21 +1209,21 @@ def canon_lift(callee_opts, callee_instance, callee, functype, args, called_as_e
1177
1209
except CoreWebAssemblyException:
1178
1210
trap()
1179
1211
1180
- [result] = lift(callee_opts, MAX_FLAT_RESULTS , ValueIter(flat_results), [ functype.result] )
1212
+ results = lift(callee_opts, MAX_FLAT_RESULTS , ValueIter(flat_results), functype.results )
1181
1213
def post_return ():
1182
1214
if callee_opts.post_return is not None :
1183
1215
callee_opts.post_return(flat_results)
1184
1216
if called_as_export:
1185
1217
callee_instance.may_enter = True
1186
1218
1187
- return (result , post_return)
1219
+ return (results , post_return)
1188
1220
```
1189
1221
There are a number of things to note about this definition:
1190
1222
1191
1223
Uncaught Core WebAssembly [ exceptions] result in a trap at component
1192
1224
boundaries. Thus, if a component wishes to signal an error, it must use some
1193
- sort of explicit type such as ` expected ` (whose ` error ` case particular
1194
- language bindings may choose to map to and from exceptions).
1225
+ sort of explicit type such as ` result ` (whose ` error ` case particular language
1226
+ bindings may choose to map to and from exceptions).
1195
1227
1196
1228
The ` called_as_export ` parameter indicates whether ` canon_lift ` is being called
1197
1229
as part of a component export or whether this ` canon_lift ` is being called
@@ -1234,10 +1266,10 @@ def canon_lower(caller_opts, caller_instance, callee, functype, flat_args):
1234
1266
flat_args = ValueIter(flat_args)
1235
1267
args = lift(caller_opts, MAX_FLAT_PARAMS , flat_args, functype.params)
1236
1268
1237
- result , post_return = callee(args)
1269
+ results , post_return = callee(args)
1238
1270
1239
1271
caller_instance.may_leave = False
1240
- flat_results = lower(caller_opts, MAX_FLAT_RESULTS , [result], [ functype.result] , flat_args)
1272
+ flat_results = lower(caller_opts, MAX_FLAT_RESULTS , results, functype.results , flat_args)
1241
1273
caller_instance.may_leave = True
1242
1274
1243
1275
post_return()
0 commit comments