js_deobfuscator/scope/
query.rs1use oxc::semantic::{Scoping, SymbolId};
4
5#[inline]
7pub fn has_writes(scoping: &Scoping, symbol_id: SymbolId) -> bool {
8 scoping
9 .get_resolved_references(symbol_id)
10 .any(|r| r.flags().is_write())
11}
12
13#[inline]
15pub fn has_reads(scoping: &Scoping, symbol_id: SymbolId) -> bool {
16 scoping
17 .get_resolved_references(symbol_id)
18 .any(|r| r.flags().is_read())
19}
20
21pub fn count_reads(scoping: &Scoping, symbol_id: SymbolId) -> usize {
23 scoping
24 .get_resolved_references(symbol_id)
25 .filter(|r| r.flags().is_read())
26 .count()
27}
28
29pub fn count_writes(scoping: &Scoping, symbol_id: SymbolId) -> usize {
31 scoping
32 .get_resolved_references(symbol_id)
33 .filter(|r| r.flags().is_write())
34 .count()
35}
36
37#[inline]
39pub fn is_const(scoping: &Scoping, symbol_id: SymbolId) -> bool {
40 scoping.symbol_flags(symbol_id).is_const_variable()
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46 use crate::scope::resolve;
47 use oxc::allocator::Allocator;
48 use oxc::ast::ast::{Expression, Statement};
49 use oxc::parser::Parser;
50 use oxc::semantic::SemanticBuilder;
51 use oxc::span::SourceType;
52
53 #[test]
54 fn test_const_no_writes() {
55 let source = "const x = 42; x; x;";
56 let alloc = Allocator::default();
57 let ret = Parser::new(&alloc, source, SourceType::mjs()).parse();
58 let scoping = SemanticBuilder::new().build(&ret.program).semantic.into_scoping();
59
60 if let Statement::ExpressionStatement(stmt) = &ret.program.body[1] {
62 if let Expression::Identifier(ident) = &stmt.expression {
63 let sym = resolve::get_reference_symbol(&scoping, ident).unwrap();
64 assert!(is_const(&scoping, sym));
65 assert!(!has_writes(&scoping, sym));
66 assert!(has_reads(&scoping, sym));
67 assert_eq!(count_reads(&scoping, sym), 2);
68 assert_eq!(count_writes(&scoping, sym), 0);
69 }
70 }
71 }
72
73 #[test]
74 fn test_let_with_writes() {
75 let source = "let y = 1; y = 2; y;";
76 let alloc = Allocator::default();
77 let ret = Parser::new(&alloc, source, SourceType::mjs()).parse();
78 let scoping = SemanticBuilder::new().build(&ret.program).semantic.into_scoping();
79
80 if let Statement::ExpressionStatement(stmt) = &ret.program.body[2] {
82 if let Expression::Identifier(ident) = &stmt.expression {
83 let sym = resolve::get_reference_symbol(&scoping, ident).unwrap();
84 assert!(!is_const(&scoping, sym));
85 assert!(has_writes(&scoping, sym));
86 assert!(has_reads(&scoping, sym));
87 assert_eq!(count_writes(&scoping, sym), 1);
88 }
89 }
90 }
91}