@@ -33,7 +33,7 @@ use core::ops::{Deref, DerefMut};
33
33
34
34
pub ( crate ) struct OperatorValidator {
35
35
pub ( super ) locals : Locals ,
36
- pub ( super ) local_inits : Vec < bool > ,
36
+ local_inits : LocalInits ,
37
37
38
38
// This is a list of flags for wasm features which are used to gate various
39
39
// instructions.
@@ -46,9 +46,6 @@ pub(crate) struct OperatorValidator {
46
46
control : Vec < Frame > ,
47
47
/// The `operands` is the current type stack.
48
48
operands : Vec < MaybeType > ,
49
- /// When local_inits is modified, the relevant index is recorded here to be
50
- /// undone when control pops
51
- inits : Vec < u32 > ,
52
49
53
50
/// Offset of the `end` instruction which emptied the `control` stack, which
54
51
/// must be the end of the function.
@@ -61,6 +58,104 @@ pub(crate) struct OperatorValidator {
61
58
pub ( crate ) pop_push_count : ( u32 , u32 ) ,
62
59
}
63
60
61
+ /// Captures the initialization of non-defaultable locals.
62
+ struct LocalInits {
63
+ /// Records if a local is already initialized.
64
+ local_inits : Vec < bool > ,
65
+ /// When `local_inits` is modified, the relevant `index` is recorded
66
+ /// here to be undone when control pops.
67
+ inits : Vec < u32 > ,
68
+ /// The index of the first non-defaultable local.
69
+ ///
70
+ /// # Note
71
+ ///
72
+ /// This is an optimization so that we only have to perform expensive
73
+ /// look-ups for locals that have a local index equal to or higher than this.
74
+ first_non_default_local : u32 ,
75
+ }
76
+
77
+ impl Default for LocalInits {
78
+ fn default ( ) -> Self {
79
+ Self {
80
+ local_inits : Vec :: default ( ) ,
81
+ inits : Vec :: default ( ) ,
82
+ first_non_default_local : u32:: MAX ,
83
+ }
84
+ }
85
+ }
86
+
87
+ impl LocalInits {
88
+ /// Defines new function local parameters.
89
+ pub fn define_params ( & mut self , count : usize ) {
90
+ let Some ( new_len) = self . local_inits . len ( ) . checked_add ( count) else {
91
+ panic ! ( "tried to define too many function locals as parameters: {count}" ) ;
92
+ } ;
93
+ self . local_inits . resize ( new_len, true ) ;
94
+ }
95
+
96
+ /// Defines `count` function locals of type `ty`.
97
+ pub fn define_locals ( & mut self , count : u32 , ty : ValType ) {
98
+ let Ok ( count) = usize:: try_from ( count) else {
99
+ panic ! ( "tried to define too many function locals: {count}" ) ;
100
+ } ;
101
+ let len = self . local_inits . len ( ) ;
102
+ let Some ( new_len) = len. checked_add ( count) else {
103
+ panic ! ( "tried to define too many function locals: {count}" ) ;
104
+ } ;
105
+ let is_defaultable = ty. is_defaultable ( ) ;
106
+ if !is_defaultable && self . first_non_default_local == u32:: MAX {
107
+ self . first_non_default_local = len as u32 ;
108
+ }
109
+ self . local_inits . resize ( new_len, is_defaultable) ;
110
+ }
111
+
112
+ /// Returns `true` if the local at `local_index` has already been initialized.
113
+ #[ inline]
114
+ pub fn is_uninit ( & self , local_index : u32 ) -> bool {
115
+ if local_index < self . first_non_default_local {
116
+ return false ;
117
+ }
118
+ !self . local_inits [ local_index as usize ]
119
+ }
120
+
121
+ /// Marks the local at `local_index` as initialized.
122
+ #[ inline]
123
+ pub fn set_init ( & mut self , local_index : u32 ) {
124
+ if self . is_uninit ( local_index) {
125
+ self . local_inits [ local_index as usize ] = true ;
126
+ self . inits . push ( local_index) ;
127
+ }
128
+ }
129
+
130
+ /// Registers a new control frame and returns its `height`.
131
+ pub fn push_ctrl ( & mut self ) -> usize {
132
+ self . inits . len ( )
133
+ }
134
+
135
+ /// Pops a control frame via its `height`.
136
+ ///
137
+ /// This uninitializes all locals that have been initialized within it.
138
+ pub fn pop_ctrl ( & mut self , height : usize ) {
139
+ for local_index in self . inits . split_off ( height) {
140
+ self . local_inits [ local_index as usize ] = false ;
141
+ }
142
+ }
143
+
144
+ /// Clears the [`LocalInits`].
145
+ ///
146
+ /// After this operation `self` will be empty and ready for reuse.
147
+ pub fn clear ( & mut self ) {
148
+ self . local_inits . clear ( ) ;
149
+ self . inits . clear ( ) ;
150
+ self . first_non_default_local = u32:: MAX ;
151
+ }
152
+
153
+ /// Returns `true` if `self` is empty.
154
+ pub fn is_empty ( & self ) -> bool {
155
+ self . local_inits . is_empty ( )
156
+ }
157
+ }
158
+
64
159
// No science was performed in the creation of this number, feel free to change
65
160
// it if you so like.
66
161
const MAX_LOCALS_TO_TRACK : usize = 50 ;
@@ -120,8 +215,7 @@ pub struct OperatorValidatorAllocations {
120
215
popped_types_tmp : Vec < MaybeType > ,
121
216
control : Vec < Frame > ,
122
217
operands : Vec < MaybeType > ,
123
- local_inits : Vec < bool > ,
124
- inits : Vec < u32 > ,
218
+ local_inits : LocalInits ,
125
219
locals_first : Vec < ValType > ,
126
220
locals_all : Vec < ( u32 , ValType ) > ,
127
221
}
@@ -226,15 +320,14 @@ impl OperatorValidator {
226
320
control,
227
321
operands,
228
322
local_inits,
229
- inits,
230
323
locals_first,
231
324
locals_all,
232
325
} = allocs;
233
326
debug_assert ! ( popped_types_tmp. is_empty( ) ) ;
234
327
debug_assert ! ( control. is_empty( ) ) ;
235
328
debug_assert ! ( operands. is_empty( ) ) ;
236
329
debug_assert ! ( local_inits. is_empty( ) ) ;
237
- debug_assert ! ( inits . is_empty( ) ) ;
330
+ debug_assert ! ( local_inits . is_empty( ) ) ;
238
331
debug_assert ! ( locals_first. is_empty( ) ) ;
239
332
debug_assert ! ( locals_all. is_empty( ) ) ;
240
333
OperatorValidator {
@@ -244,7 +337,6 @@ impl OperatorValidator {
244
337
all : locals_all,
245
338
} ,
246
339
local_inits,
247
- inits,
248
340
features : * features,
249
341
popped_types_tmp,
250
342
operands,
@@ -293,8 +385,8 @@ impl OperatorValidator {
293
385
if let CompositeInnerType :: Func ( func_ty) = & sub_ty. composite_type . inner {
294
386
for ty in func_ty. params ( ) {
295
387
ret. locals . define ( 1 , * ty) ;
296
- ret. local_inits . push ( true ) ;
297
388
}
389
+ ret. local_inits . define_params ( func_ty. params ( ) . len ( ) ) ;
298
390
} else {
299
391
bail ! ( offset, "expected func type at index {ty}, found {sub_ty}" )
300
392
}
@@ -343,8 +435,7 @@ impl OperatorValidator {
343
435
offset,
344
436
) ) ;
345
437
}
346
- self . local_inits
347
- . resize ( self . local_inits . len ( ) + count as usize , ty. is_defaultable ( ) ) ;
438
+ self . local_inits . define_locals ( count, ty) ;
348
439
Ok ( ( ) )
349
440
}
350
441
@@ -418,7 +509,7 @@ impl OperatorValidator {
418
509
format_err ! ( offset, "operators remaining after end of function" )
419
510
}
420
511
421
- pub fn into_allocations ( self ) -> OperatorValidatorAllocations {
512
+ pub fn into_allocations ( mut self ) -> OperatorValidatorAllocations {
422
513
fn clear < T > ( mut tmp : Vec < T > ) -> Vec < T > {
423
514
tmp. clear ( ) ;
424
515
tmp
@@ -427,8 +518,10 @@ impl OperatorValidator {
427
518
popped_types_tmp : clear ( self . popped_types_tmp ) ,
428
519
control : clear ( self . control ) ,
429
520
operands : clear ( self . operands ) ,
430
- local_inits : clear ( self . local_inits ) ,
431
- inits : clear ( self . inits ) ,
521
+ local_inits : {
522
+ self . local_inits . clear ( ) ;
523
+ self . local_inits
524
+ } ,
432
525
locals_first : clear ( self . locals . first ) ,
433
526
locals_all : clear ( self . locals . all ) ,
434
527
}
@@ -816,7 +909,7 @@ where
816
909
// Push a new frame which has a snapshot of the height of the current
817
910
// operand stack.
818
911
let height = self . operands . len ( ) ;
819
- let init_height = self . inits . len ( ) ;
912
+ let init_height = self . local_inits . push_ctrl ( ) ;
820
913
self . control . push ( Frame {
821
914
kind,
822
915
block_type : ty,
@@ -848,9 +941,7 @@ where
848
941
let init_height = frame. init_height ;
849
942
850
943
// reset_locals in the spec
851
- for init in self . inits . split_off ( init_height) {
852
- self . local_inits [ init as usize ] = false ;
853
- }
944
+ self . local_inits . pop_ctrl ( init_height) ;
854
945
855
946
// Pop all the result types, in reverse order, from the operand stack.
856
947
// These types will, possibly, be transferred to the next frame.
@@ -2034,7 +2125,7 @@ where
2034
2125
fn visit_local_get ( & mut self , local_index : u32 ) -> Self :: Output {
2035
2126
let ty = self . local ( local_index) ?;
2036
2127
debug_assert_type_indices_are_ids ( ty) ;
2037
- if ! self . local_inits [ local_index as usize ] {
2128
+ if self . local_inits . is_uninit ( local_index) {
2038
2129
bail ! ( self . offset, "uninitialized local: {}" , local_index) ;
2039
2130
}
2040
2131
self . push_operand ( ty) ?;
@@ -2043,20 +2134,13 @@ where
2043
2134
fn visit_local_set ( & mut self , local_index : u32 ) -> Self :: Output {
2044
2135
let ty = self . local ( local_index) ?;
2045
2136
self . pop_operand ( Some ( ty) ) ?;
2046
- if !self . local_inits [ local_index as usize ] {
2047
- self . local_inits [ local_index as usize ] = true ;
2048
- self . inits . push ( local_index) ;
2049
- }
2137
+ self . local_inits . set_init ( local_index) ;
2050
2138
Ok ( ( ) )
2051
2139
}
2052
2140
fn visit_local_tee ( & mut self , local_index : u32 ) -> Self :: Output {
2053
2141
let expected_ty = self . local ( local_index) ?;
2054
2142
self . pop_operand ( Some ( expected_ty) ) ?;
2055
- if !self . local_inits [ local_index as usize ] {
2056
- self . local_inits [ local_index as usize ] = true ;
2057
- self . inits . push ( local_index) ;
2058
- }
2059
-
2143
+ self . local_inits . set_init ( local_index) ;
2060
2144
self . push_operand ( expected_ty) ?;
2061
2145
Ok ( ( ) )
2062
2146
}
@@ -4841,7 +4925,7 @@ where
4841
4925
}
4842
4926
// Start a new frame and push `exnref` value.
4843
4927
let height = self . operands . len ( ) ;
4844
- let init_height = self . inits . len ( ) ;
4928
+ let init_height = self . local_inits . push_ctrl ( ) ;
4845
4929
self . control . push ( Frame {
4846
4930
kind : FrameKind :: LegacyCatch ,
4847
4931
block_type : frame. block_type ,
@@ -4890,7 +4974,7 @@ where
4890
4974
bail ! ( self . offset, "catch_all found outside of a `try` block" ) ;
4891
4975
}
4892
4976
let height = self . operands . len ( ) ;
4893
- let init_height = self . inits . len ( ) ;
4977
+ let init_height = self . local_inits . push_ctrl ( ) ;
4894
4978
self . control . push ( Frame {
4895
4979
kind : FrameKind :: LegacyCatchAll ,
4896
4980
block_type : frame. block_type ,
0 commit comments