@@ -1134,6 +1134,7 @@ Given the above closure arguments, `canon_lift` is defined:
1134
1134
``` python
1135
1135
def canon_lift (callee_opts , callee_instance , callee , functype , args ):
1136
1136
trap_if(not callee_instance.may_enter)
1137
+ callee_instance.may_enter = False
1137
1138
1138
1139
assert (callee_instance.may_leave)
1139
1140
callee_instance.may_leave = False
@@ -1145,12 +1146,11 @@ def canon_lift(callee_opts, callee_instance, callee, functype, args):
1145
1146
except CoreWebAssemblyException:
1146
1147
trap()
1147
1148
1148
- callee_instance.may_enter = False
1149
1149
[result] = lift(callee_opts, MAX_FLAT_RESULTS , ValueIter(flat_results), [functype.result])
1150
1150
def post_return ():
1151
- callee_instance.may_enter = True
1152
1151
if callee_opts.post_return is not None :
1153
1152
callee_opts.post_return(flat_results)
1153
+ callee_instance.may_enter = True
1154
1154
1155
1155
return (result, post_return)
1156
1156
```
@@ -1161,6 +1161,14 @@ boundaries. Thus, if a component wishes to signal an error, it must use some
1161
1161
sort of explicit type such as ` expected ` (whose ` error ` case particular
1162
1162
language bindings may choose to map to and from exceptions).
1163
1163
1164
+ The clearing of ` may_enter ` for the entire duration of ` canon_lift ` and the
1165
+ fact that ` canon_lift ` brackets all calls into a component ensure that
1166
+ components cannot be reentered, which is a [ component invariant] . Furthermore,
1167
+ because ` may_enter ` is not cleared on the exceptional exit path taken by
1168
+ ` trap() ` , if there is a trap during Core WebAssembly execution or
1169
+ lifting/lowering, the component is left permanently un-enterable, ensuring the
1170
+ lockdown-after-trap [ component invariant] .
1171
+
1164
1172
The contract assumed by ` canon_lift ` (and ensured by ` canon_lower ` below) is
1165
1173
that the caller of ` canon_lift ` * must* call ` post_return ` right after lowering
1166
1174
` result ` . This ordering ensures that the engine can reliably copy directly from
@@ -1170,22 +1178,12 @@ the callee's linear memory (read by `lift`) into the caller's linear memory
1170
1178
freed and so the engine would need to eagerly make an intermediate copy in
1171
1179
` lift ` .
1172
1180
1173
- Even assuming this ` post_return ` contract, if the callee could be re-entered
1174
- by the caller in the middle of the caller's ` lower ` (e.g., via ` realloc ` ), then
1175
- either the engine has to make an eager intermediate copy in ` lift ` * or* the
1176
- Canonical ABI would have to specify a precise interleaving of side effects
1177
- which is more complicated and would inhibit some optimizations. Instead, the
1178
- ` may_enter ` guard set before ` lift ` and cleared in ` post_return ` prevents this
1179
- re-entrance. Thus, it is the combination of ` post_return ` and the re-entrance
1180
- guard that ensures ` lift ` does not need to make an eager copy.
1181
-
1182
1181
The ` may_leave ` guard wrapping the lowering of parameters conservatively
1183
- ensures that ` realloc ` calls during lowering do not accidentally call imports
1184
- that accidentally re-enter the instance that lifted the same parameters.
1185
- While the ` may_enter ` guards of * those* component instances would also prevent
1186
- this re-entrance, it would be an error that only manifested in certain
1187
- component linking configurations, hence the eager error helps ensure
1188
- compositionality.
1182
+ ensures that ` realloc ` calls during lowering do not call imports that
1183
+ indirectly re-enter the instance that lifted the same parameters. While the
1184
+ ` may_enter ` guards of * those* component instances would also prevent this
1185
+ re-entrance, it would be an error that only manifested in certain component
1186
+ linking configurations, hence the eager error helps ensure compositionality.
1189
1187
1190
1188
1191
1189
### ` lower `
@@ -1210,9 +1208,6 @@ and, when called from Core WebAssembly code, calls `canon_lower`, which is defin
1210
1208
def canon_lower (caller_opts , caller_instance , callee , functype , flat_args ):
1211
1209
trap_if(not caller_instance.may_leave)
1212
1210
1213
- assert (caller_instance.may_enter)
1214
- caller_instance.may_enter = False
1215
-
1216
1211
flat_args = ValueIter(flat_args)
1217
1212
args = lift(caller_opts, MAX_FLAT_PARAMS , flat_args, functype.params)
1218
1213
@@ -1224,15 +1219,10 @@ def canon_lower(caller_opts, caller_instance, callee, functype, flat_args):
1224
1219
1225
1220
post_return()
1226
1221
1227
- caller_instance.may_enter = True
1228
1222
return flat_results
1229
1223
```
1230
1224
The definitions of ` canon_lift ` and ` canon_lower ` are mostly symmetric (swapping
1231
1225
lifting and lowering), with a few exceptions:
1232
- * The calling instance cannot be re-entered over the course of the entire call,
1233
- not just while lifting the parameters. This ensures not just the needs of the
1234
- Canonical ABI, but the general non-re-entrance expectations outlined in the
1235
- [ component invariants] .
1236
1226
* The caller does not need a ` post-return ` function since the Core WebAssembly
1237
1227
caller simply regains control when ` canon_lower ` returns, allowing it to free
1238
1228
(or not) any memory passed as ` flat_args ` .
0 commit comments