js_deobfuscator/scope/
resolve.rs1use oxc::ast::ast::{
6 BindingIdentifier, Function, IdentifierReference, VariableDeclarator,
7};
8use oxc::semantic::{Scoping, SymbolId};
9
10#[inline]
14pub fn get_reference_symbol(scoping: &Scoping, ident: &IdentifierReference) -> Option<SymbolId> {
15 let ref_id = ident.reference_id.get()?;
16 scoping.get_reference(ref_id).symbol_id()
17}
18
19#[inline]
21pub fn get_binding_symbol(binding: &BindingIdentifier) -> Option<SymbolId> {
22 binding.symbol_id.get()
23}
24
25#[inline]
29pub fn get_declarator_symbol(decl: &VariableDeclarator) -> Option<SymbolId> {
30 decl.id.get_binding_identifier().and_then(|b| b.symbol_id.get())
31}
32
33#[inline]
35pub fn get_function_symbol(func: &Function) -> Option<SymbolId> {
36 func.id.as_ref().and_then(|id| id.symbol_id.get())
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use oxc::allocator::Allocator;
43 use oxc::ast::ast::Statement;
44 use oxc::parser::Parser;
45 use oxc::semantic::SemanticBuilder;
46 use oxc::span::SourceType;
47
48 #[test]
49 fn test_resolve_const() {
50 let source = "const x = 42; x;";
51 let alloc = Allocator::default();
52 let ret = Parser::new(&alloc, source, SourceType::mjs()).parse();
53 let scoping = SemanticBuilder::new().build(&ret.program).semantic.into_scoping();
54
55 if let Statement::ExpressionStatement(stmt) = &ret.program.body[1] {
57 if let oxc::ast::ast::Expression::Identifier(ident) = &stmt.expression {
58 let sym = get_reference_symbol(&scoping, ident);
59 assert!(sym.is_some(), "should resolve const x");
60 }
61 }
62 }
63
64 #[test]
65 fn test_global_is_none() {
66 let source = "console.log(1);";
67 let alloc = Allocator::default();
68 let ret = Parser::new(&alloc, source, SourceType::mjs()).parse();
69 let scoping = SemanticBuilder::new().build(&ret.program).semantic.into_scoping();
70
71 if let Statement::ExpressionStatement(stmt) = &ret.program.body[0] {
72 if let oxc::ast::ast::Expression::CallExpression(call) = &stmt.expression {
73 if let oxc::ast::ast::Expression::StaticMemberExpression(member) = &call.callee {
74 if let oxc::ast::ast::Expression::Identifier(ident) = &member.object {
75 let sym = get_reference_symbol(&scoping, ident);
76 assert!(sym.is_none(), "console should be unresolved (global)");
77 }
78 }
79 }
80 }
81 }
82
83 #[test]
84 fn test_get_declarator_symbol() {
85 let alloc = Allocator::default();
86 let ret = Parser::new(&alloc, "const x = 1;", SourceType::mjs()).parse();
87 let _scoping = SemanticBuilder::new().build(&ret.program).semantic.into_scoping();
88
89 if let Statement::VariableDeclaration(decl) = &ret.program.body[0] {
90 let sym = get_declarator_symbol(&decl.declarations[0]);
91 assert!(sym.is_some(), "declarator should have symbol ID");
92 }
93 }
94
95 #[test]
96 fn test_get_function_symbol() {
97 let alloc = Allocator::default();
98 let ret = Parser::new(&alloc, "function foo() {}", SourceType::mjs()).parse();
99 let _scoping = SemanticBuilder::new().build(&ret.program).semantic.into_scoping();
100
101 if let Statement::FunctionDeclaration(func) = &ret.program.body[0] {
102 let sym = get_function_symbol(func);
103 assert!(sym.is_some(), "named function should have symbol ID");
104 }
105 }
106}