@@ -37,6 +37,9 @@ pub struct JsBuilder<'a, 'b> {
37
37
/// JS functions, etc.
38
38
cx : & ' a mut Context < ' b > ,
39
39
40
+ /// A debug name for the function being generated, used for error messages
41
+ debug_name : & ' a str ,
42
+
40
43
/// The "prelude" of the function, or largely just the JS function we've
41
44
/// built so far.
42
45
prelude : String ,
@@ -121,6 +124,7 @@ impl<'a, 'b> Builder<'a, 'b> {
121
124
asyncness : bool ,
122
125
variadic : bool ,
123
126
generate_jsdoc : bool ,
127
+ debug_name : & str ,
124
128
) -> Result < JsFunction , Error > {
125
129
if self
126
130
. cx
@@ -138,7 +142,7 @@ impl<'a, 'b> Builder<'a, 'b> {
138
142
// If this is a method then we're generating this as part of a class
139
143
// method, so the leading parameter is the this pointer stored on
140
144
// the JS object, so synthesize that here.
141
- let mut js = JsBuilder :: new ( self . cx ) ;
145
+ let mut js = JsBuilder :: new ( self . cx , debug_name ) ;
142
146
if let Some ( consumes_self) = self . method {
143
147
let _ = params. next ( ) ;
144
148
if js. cx . config . debug {
@@ -184,7 +188,12 @@ impl<'a, 'b> Builder<'a, 'b> {
184
188
) ?;
185
189
}
186
190
187
- assert_eq ! ( js. stack. len( ) , adapter. results. len( ) ) ;
191
+ assert_eq ! (
192
+ js. stack. len( ) ,
193
+ adapter. results. len( ) ,
194
+ "stack size mismatch for {}" ,
195
+ debug_name
196
+ ) ;
188
197
match js. stack . len ( ) {
189
198
0 => { }
190
199
1 => {
@@ -422,9 +431,10 @@ impl<'a, 'b> Builder<'a, 'b> {
422
431
}
423
432
424
433
impl < ' a , ' b > JsBuilder < ' a , ' b > {
425
- pub fn new ( cx : & ' a mut Context < ' b > ) -> JsBuilder < ' a , ' b > {
434
+ pub fn new ( cx : & ' a mut Context < ' b > , debug_name : & ' a str ) -> JsBuilder < ' a , ' b > {
426
435
JsBuilder {
427
436
cx,
437
+ debug_name,
428
438
args : Vec :: new ( ) ,
429
439
tmp : 0 ,
430
440
pre_try : String :: new ( ) ,
@@ -463,7 +473,10 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
463
473
}
464
474
465
475
fn pop ( & mut self ) -> String {
466
- self . stack . pop ( ) . unwrap ( )
476
+ match self . stack . pop ( ) {
477
+ Some ( s) => s,
478
+ None => panic ! ( "popping an empty stack in {}" , self . debug_name) ,
479
+ }
467
480
}
468
481
469
482
fn push ( & mut self , arg : String ) {
@@ -920,6 +933,44 @@ fn instruction(
920
933
js. push ( format ! ( "isLikeNone({0}) ? {1} : {0}" , val, hole) ) ;
921
934
}
922
935
936
+ Instruction :: F64FromOptionSentinelInt { signed } => {
937
+ let val = js. pop ( ) ;
938
+ js. cx . expose_is_like_none ( ) ;
939
+ js. assert_optional_number ( & val) ;
940
+
941
+ // We need to convert the given number to a 32-bit integer before
942
+ // passing it to the ABI for 2 reasons:
943
+ // 1. Rust's behavior for `value_f64 as i32/u32` is different from
944
+ // the WebAssembly behavior for values outside the 32-bit range.
945
+ // We could implement this behavior in Rust too, but it's easier
946
+ // to do it in JS.
947
+ // 2. If we allowed values outside the 32-bit range, the sentinel
948
+ // value itself would be allowed. This would make it impossible
949
+ // to distinguish between the sentinel value and a valid value.
950
+ //
951
+ // To perform the actual conversion, we use JS bit shifts. Handily,
952
+ // >> and >>> perform a conversion to i32 and u32 respectively
953
+ // to apply the bit shift, so we can use e.g. x >>> 0 to convert to
954
+ // u32.
955
+
956
+ let op = if * signed { ">>" } else { ">>>" } ;
957
+ js. push ( format ! ( "isLikeNone({val}) ? 0x100000001 : ({val}) {op} 0" ) ) ;
958
+ }
959
+ Instruction :: F64FromOptionSentinelF32 => {
960
+ let val = js. pop ( ) ;
961
+ js. cx . expose_is_like_none ( ) ;
962
+ js. assert_optional_number ( & val) ;
963
+
964
+ // Similar to the above 32-bit integer variant, we convert the
965
+ // number to a 32-bit *float* before passing it to the ABI. This
966
+ // ensures consistent behavior with WebAssembly and makes it
967
+ // possible to use a sentinel value.
968
+
969
+ js. push ( format ! (
970
+ "isLikeNone({val}) ? 0x100000001 : Math.fround({val})"
971
+ ) ) ;
972
+ }
973
+
923
974
Instruction :: FromOptionNative { ty } => {
924
975
let val = js. pop ( ) ;
925
976
js. cx . expose_is_like_none ( ) ;
@@ -1277,6 +1328,11 @@ fn instruction(
1277
1328
) ) ;
1278
1329
}
1279
1330
1331
+ Instruction :: OptionF64Sentinel => {
1332
+ let val = js. pop ( ) ;
1333
+ js. push ( format ! ( "{0} === 0x100000001 ? undefined : {0}" , val) ) ;
1334
+ }
1335
+
1280
1336
Instruction :: OptionU32Sentinel => {
1281
1337
let val = js. pop ( ) ;
1282
1338
js. push ( format ! ( "{0} === 0xFFFFFF ? undefined : {0}" , val) ) ;
0 commit comments