1
1
#![ deny( unused_crate_dependencies) ]
2
2
3
+ use serde:: { Deserialize , Serialize } ;
3
4
use std:: {
4
5
collections:: { HashMap , HashSet } ,
5
6
sync:: Arc ,
@@ -44,9 +45,8 @@ pub type MacroCallback =
44
45
pub struct Macros < ' a > {
45
46
/// Mapping of imported identifiers to import metadata.
46
47
macros : HashMap < Id , MacroImport > ,
47
- constants : HashMap < Id , Result < JsValue , Span > > ,
48
+ evaluator : Evaluator < ' a > ,
48
49
callback : MacroCallback ,
49
- source_map : & ' a SourceMap ,
50
50
errors : & ' a mut Vec < MacroError > ,
51
51
load_errors : HashSet < String > ,
52
52
assignment_span : Option < Span > ,
@@ -70,10 +70,9 @@ impl<'a> Macros<'a> {
70
70
) -> Self {
71
71
Macros {
72
72
macros : HashMap :: new ( ) ,
73
- constants : HashMap :: new ( ) ,
73
+ evaluator : Evaluator :: new ( source_map ) ,
74
74
load_errors : HashSet :: new ( ) ,
75
75
callback,
76
- source_map,
77
76
errors,
78
77
assignment_span : None ,
79
78
in_call : false ,
@@ -137,7 +136,7 @@ impl<'a> Macros<'a> {
137
136
// Try to statically evaluate all of the function arguments.
138
137
let mut args = Vec :: with_capacity ( call. args . len ( ) ) ;
139
138
for arg in & call. args {
140
- match self . eval ( & * arg. expr ) {
139
+ match self . evaluator . eval ( & * arg. expr ) {
141
140
Ok ( val) => {
142
141
if arg. spread . is_none ( ) {
143
142
args. push ( val) ;
@@ -154,13 +153,13 @@ impl<'a> Macros<'a> {
154
153
}
155
154
156
155
// If that was successful, call the function callback (on the JS thread).
157
- let loc = self . source_map . lookup_char_pos ( call. span . lo ) ;
156
+ let loc = self . evaluator . source_map . lookup_char_pos ( call. span . lo ) ;
158
157
let loc = Location {
159
158
line : loc. line as u32 ,
160
159
col : loc. col_display as u32 ,
161
160
} ;
162
161
match ( self . callback ) ( src. clone ( ) , export, args, loc) {
163
- Ok ( val) => Ok ( self . value_to_expr ( val) ?) ,
162
+ Ok ( val) => Ok ( self . evaluator . value_to_expr ( val) ?) ,
164
163
Err ( err) => match err {
165
164
MacroError :: LoadError ( err, _) => {
166
165
self . load_errors . insert ( src) ;
@@ -224,7 +223,7 @@ impl<'a> Fold for Macros<'a> {
224
223
let imported = match & member. prop {
225
224
MemberProp :: Ident ( id) => id. sym . to_string ( ) ,
226
225
MemberProp :: Computed ( s) => {
227
- if let Ok ( JsValue :: String ( s) ) = self . eval ( & s. expr ) {
226
+ if let Ok ( JsValue :: String ( s) ) = self . evaluator . eval ( & s. expr ) {
228
227
s
229
228
} else {
230
229
break ' block;
@@ -263,8 +262,8 @@ impl<'a> Fold for Macros<'a> {
263
262
if node. kind == VarDeclKind :: Const {
264
263
for decl in & node. decls {
265
264
if let Some ( expr) = & decl. init {
266
- let val = self . eval ( & * expr) ;
267
- self . eval_pat ( val, & decl. name ) ;
265
+ let val = self . evaluator . eval ( & * expr) ;
266
+ self . evaluator . eval_pat ( val, & decl. name ) ;
268
267
}
269
268
}
270
269
}
@@ -286,7 +285,7 @@ impl<'a> Fold for Macros<'a> {
286
285
// Error when re-assigning a property of a constant that's used in a macro.
287
286
let node = node. fold_children_with ( self ) ;
288
287
if let Expr :: Ident ( id) = & * node. obj {
289
- if let Some ( constant) = self . constants . get_mut ( & id. to_id ( ) ) {
288
+ if let Some ( constant) = self . evaluator . constants . get_mut ( & id. to_id ( ) ) {
290
289
if constant. is_ok ( ) {
291
290
* constant = Err ( assignment_span. clone ( ) ) ;
292
291
}
@@ -299,8 +298,9 @@ impl<'a> Fold for Macros<'a> {
299
298
// If the member expression evaluates to an object, continue traversing so we error in fold_ident.
300
299
// Otherwise, return early to allow other properties to be accessed without error.
301
300
let value = self
301
+ . evaluator
302
302
. eval ( & * node. obj )
303
- . and_then ( |obj| self . eval_member_prop ( obj, & node) ) ;
303
+ . and_then ( |obj| self . evaluator . eval_member_prop ( obj, & node) ) ;
304
304
if !matches ! (
305
305
value,
306
306
Err ( ..) | Ok ( JsValue :: Object ( ..) | JsValue :: Array ( ..) )
@@ -314,7 +314,7 @@ impl<'a> Fold for Macros<'a> {
314
314
315
315
fn fold_ident ( & mut self , node : Ident ) -> Ident {
316
316
if self . in_call {
317
- if let Some ( constant) = self . constants . get_mut ( & node. to_id ( ) ) {
317
+ if let Some ( constant) = self . evaluator . constants . get_mut ( & node. to_id ( ) ) {
318
318
if matches ! ( constant, Ok ( JsValue :: Object ( ..) | JsValue :: Array ( ..) ) ) {
319
319
// Mark access to constant object inside a call as an error since it could potentially be mutated.
320
320
* constant = Err ( node. span . clone ( ) ) ;
@@ -362,7 +362,8 @@ fn handle_error(result: Result<Expr, MacroError>, errors: &mut Vec<MacroError>)
362
362
}
363
363
364
364
/// A type that represents a basic JS value.
365
- #[ derive( Clone , Debug ) ]
365
+ #[ derive( Clone , Debug , PartialEq , Serialize , Deserialize ) ]
366
+ #[ serde( untagged) ]
366
367
pub enum JsValue {
367
368
Undefined ,
368
369
Null ,
@@ -375,9 +376,21 @@ pub enum JsValue {
375
376
Function ( String ) ,
376
377
}
377
378
378
- impl < ' a > Macros < ' a > {
379
+ pub struct Evaluator < ' a > {
380
+ constants : HashMap < Id , Result < JsValue , Span > > ,
381
+ source_map : & ' a SourceMap ,
382
+ }
383
+
384
+ impl < ' a > Evaluator < ' a > {
385
+ pub fn new ( source_map : & ' a SourceMap ) -> Evaluator < ' _ > {
386
+ Evaluator {
387
+ constants : HashMap :: new ( ) ,
388
+ source_map,
389
+ }
390
+ }
391
+
379
392
/// Statically evaluate a JS expression to a value, if possible.
380
- fn eval ( & self , expr : & Expr ) -> Result < JsValue , Span > {
393
+ pub fn eval ( & self , expr : & Expr ) -> Result < JsValue , Span > {
381
394
match expr. unwrap_parens ( ) {
382
395
Expr :: Lit ( lit) => match lit {
383
396
Lit :: Null ( _) => Ok ( JsValue :: Null ) ,
@@ -437,49 +450,7 @@ impl<'a> Macros<'a> {
437
450
}
438
451
Ok ( JsValue :: Array ( res) )
439
452
}
440
- Expr :: Object ( obj) => {
441
- let mut res = IndexMap :: with_capacity ( obj. props . len ( ) ) ;
442
- for prop in & obj. props {
443
- match prop {
444
- PropOrSpread :: Prop ( prop) => match & * * prop {
445
- Prop :: KeyValue ( kv) => {
446
- let v = self . eval ( & * kv. value ) ?;
447
- let k = match & kv. key {
448
- PropName :: Ident ( IdentName { sym, .. } )
449
- | PropName :: Str ( Str { value : sym, .. } ) => sym. to_string ( ) ,
450
- PropName :: Num ( n) => n. value . to_string ( ) ,
451
- PropName :: Computed ( c) => match self . eval ( & * c. expr ) {
452
- Err ( e) => return Err ( e) ,
453
- Ok ( JsValue :: String ( s) ) => s,
454
- Ok ( JsValue :: Number ( n) ) => n. to_string ( ) ,
455
- Ok ( JsValue :: Bool ( b) ) => b. to_string ( ) ,
456
- _ => return Err ( c. span ) ,
457
- } ,
458
- PropName :: BigInt ( v) => return Err ( v. span ) ,
459
- } ;
460
-
461
- res. insert ( k. to_string ( ) , v) ;
462
- }
463
- Prop :: Shorthand ( s) => {
464
- if let Some ( val) = self . constants . get ( & s. to_id ( ) ) {
465
- res. insert ( s. sym . to_string ( ) , val. clone ( ) ?) ;
466
- } else {
467
- return Err ( s. span ) ;
468
- }
469
- }
470
- _ => return Err ( obj. span ) ,
471
- } ,
472
- PropOrSpread :: Spread ( spread) => {
473
- let v = self . eval ( & * spread. expr ) ?;
474
- match v {
475
- JsValue :: Object ( o) => res. extend ( o) ,
476
- _ => return Err ( obj. span ) ,
477
- }
478
- }
479
- }
480
- }
481
- Ok ( JsValue :: Object ( res) )
482
- }
453
+ Expr :: Object ( obj) => self . eval_object ( obj) ,
483
454
Expr :: Bin ( bin) => match ( bin. op , self . eval ( & * bin. left ) , self . eval ( & * bin. right ) ) {
484
455
( BinaryOp :: Add , Ok ( JsValue :: String ( a) ) , Ok ( JsValue :: String ( b) ) ) => {
485
456
Ok ( JsValue :: String ( format ! ( "{}{}" , a, b) ) )
@@ -676,7 +647,52 @@ impl<'a> Macros<'a> {
676
647
}
677
648
}
678
649
679
- fn eval_member_prop ( & self , obj : JsValue , member : & MemberExpr ) -> Result < JsValue , Span > {
650
+ pub fn eval_object ( & self , obj : & ObjectLit ) -> Result < JsValue , Span > {
651
+ let mut res = IndexMap :: with_capacity ( obj. props . len ( ) ) ;
652
+ for prop in & obj. props {
653
+ match prop {
654
+ PropOrSpread :: Prop ( prop) => match & * * prop {
655
+ Prop :: KeyValue ( kv) => {
656
+ let v = self . eval ( & * kv. value ) ?;
657
+ let k = match & kv. key {
658
+ PropName :: Ident ( IdentName { sym, .. } ) | PropName :: Str ( Str { value : sym, .. } ) => {
659
+ sym. to_string ( )
660
+ }
661
+ PropName :: Num ( n) => n. value . to_string ( ) ,
662
+ PropName :: Computed ( c) => match self . eval ( & * c. expr ) {
663
+ Err ( e) => return Err ( e) ,
664
+ Ok ( JsValue :: String ( s) ) => s,
665
+ Ok ( JsValue :: Number ( n) ) => n. to_string ( ) ,
666
+ Ok ( JsValue :: Bool ( b) ) => b. to_string ( ) ,
667
+ _ => return Err ( c. span ) ,
668
+ } ,
669
+ PropName :: BigInt ( v) => return Err ( v. span ) ,
670
+ } ;
671
+
672
+ res. insert ( k. to_string ( ) , v) ;
673
+ }
674
+ Prop :: Shorthand ( s) => {
675
+ if let Some ( val) = self . constants . get ( & s. to_id ( ) ) {
676
+ res. insert ( s. sym . to_string ( ) , val. clone ( ) ?) ;
677
+ } else {
678
+ return Err ( s. span ) ;
679
+ }
680
+ }
681
+ _ => return Err ( obj. span ) ,
682
+ } ,
683
+ PropOrSpread :: Spread ( spread) => {
684
+ let v = self . eval ( & * spread. expr ) ?;
685
+ match v {
686
+ JsValue :: Object ( o) => res. extend ( o) ,
687
+ _ => return Err ( obj. span ) ,
688
+ }
689
+ }
690
+ }
691
+ }
692
+ Ok ( JsValue :: Object ( res) )
693
+ }
694
+
695
+ pub fn eval_member_prop ( & self , obj : JsValue , member : & MemberExpr ) -> Result < JsValue , Span > {
680
696
match & member. prop {
681
697
MemberProp :: Ident ( id) => obj. get_id ( id. as_ref ( ) ) . ok_or ( member. span ) ,
682
698
MemberProp :: Computed ( prop) => {
@@ -688,7 +704,7 @@ impl<'a> Macros<'a> {
688
704
}
689
705
690
706
/// Convert JS value to AST.
691
- fn value_to_expr ( & self , value : JsValue ) -> Result < Expr , MacroError > {
707
+ pub fn value_to_expr ( & self , value : JsValue ) -> Result < Expr , MacroError > {
692
708
Ok ( match value {
693
709
JsValue :: Null => Expr :: Lit ( Lit :: Null ( Null :: dummy ( ) ) ) ,
694
710
JsValue :: Undefined => Expr :: Ident ( Ident :: new_no_ctxt ( js_word ! ( "undefined" ) , DUMMY_SP ) ) ,
@@ -764,7 +780,7 @@ impl<'a> Macros<'a> {
764
780
} )
765
781
}
766
782
767
- fn eval_pat ( & mut self , value : Result < JsValue , Span > , pat : & Pat ) {
783
+ pub fn eval_pat ( & mut self , value : Result < JsValue , Span > , pat : & Pat ) {
768
784
match pat {
769
785
Pat :: Ident ( name) => {
770
786
self . constants . insert ( name. to_id ( ) , value) ;
0 commit comments