use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use tsz_parser::NodeIndex;
pub mod lib_loader;
pub mod module_resolution_debug;
pub mod state;
mod state_binding;
mod state_flow_helpers;
mod state_import_export;
mod state_lib_merge;
mod state_module_binding;
mod state_node_binding;
pub use state::{
BinderOptions, BinderState, DeclarationArenaMap, FileFeatures, GlobalAugmentation, LibContext,
ModuleAugmentation, ValidationError,
};
pub mod symbol_flags {
pub const NONE: u32 = 0;
pub const FUNCTION_SCOPED_VARIABLE: u32 = 1 << 0; pub const BLOCK_SCOPED_VARIABLE: u32 = 1 << 1; pub const PROPERTY: u32 = 1 << 2; pub const ENUM_MEMBER: u32 = 1 << 3; pub const FUNCTION: u32 = 1 << 4; pub const CLASS: u32 = 1 << 5; pub const INTERFACE: u32 = 1 << 6; pub const CONST_ENUM: u32 = 1 << 7; pub const REGULAR_ENUM: u32 = 1 << 8; pub const VALUE_MODULE: u32 = 1 << 9; pub const NAMESPACE_MODULE: u32 = 1 << 10; pub const TYPE_LITERAL: u32 = 1 << 11; pub const OBJECT_LITERAL: u32 = 1 << 12; pub const METHOD: u32 = 1 << 13; pub const CONSTRUCTOR: u32 = 1 << 14; pub const GET_ACCESSOR: u32 = 1 << 15; pub const SET_ACCESSOR: u32 = 1 << 16; pub const SIGNATURE: u32 = 1 << 17; pub const TYPE_PARAMETER: u32 = 1 << 18; pub const TYPE_ALIAS: u32 = 1 << 19; pub const EXPORT_VALUE: u32 = 1 << 20; pub const ALIAS: u32 = 1 << 21; pub const PROTOTYPE: u32 = 1 << 22; pub const EXPORT_STAR: u32 = 1 << 23; pub const OPTIONAL: u32 = 1 << 24; pub const TRANSIENT: u32 = 1 << 25; pub const ASSIGNMENT: u32 = 1 << 26; pub const MODULE_EXPORTS: u32 = 1 << 27; pub const PRIVATE: u32 = 1 << 28; pub const PROTECTED: u32 = 1 << 29; pub const ABSTRACT: u32 = 1 << 30; pub const STATIC: u32 = 1 << 31;
pub const ENUM: u32 = REGULAR_ENUM | CONST_ENUM;
pub const VARIABLE: u32 = FUNCTION_SCOPED_VARIABLE | BLOCK_SCOPED_VARIABLE;
pub const VALUE: u32 = VARIABLE
| PROPERTY
| ENUM_MEMBER
| OBJECT_LITERAL
| FUNCTION
| CLASS
| ENUM
| VALUE_MODULE
| METHOD
| GET_ACCESSOR
| SET_ACCESSOR;
pub const TYPE: u32 =
CLASS | INTERFACE | ENUM | ENUM_MEMBER | TYPE_LITERAL | TYPE_PARAMETER | TYPE_ALIAS;
pub const NAMESPACE: u32 = VALUE_MODULE | NAMESPACE_MODULE | ENUM;
pub const MODULE: u32 = VALUE_MODULE | NAMESPACE_MODULE;
pub const ACCESSOR: u32 = GET_ACCESSOR | SET_ACCESSOR;
pub const FUNCTION_SCOPED_VARIABLE_EXCLUDES: u32 = VALUE & !FUNCTION_SCOPED_VARIABLE;
pub const BLOCK_SCOPED_VARIABLE_EXCLUDES: u32 = VALUE;
pub const PARAMETER_EXCLUDES: u32 = VALUE;
pub const PROPERTY_EXCLUDES: u32 = NONE;
pub const ENUM_MEMBER_EXCLUDES: u32 = VALUE | TYPE;
pub const FUNCTION_EXCLUDES: u32 = VALUE & !FUNCTION & !VALUE_MODULE & !CLASS;
pub const CLASS_EXCLUDES: u32 = (VALUE | TYPE) & !VALUE_MODULE & !INTERFACE & !FUNCTION;
pub const INTERFACE_EXCLUDES: u32 = TYPE & !INTERFACE & !CLASS;
pub const REGULAR_ENUM_EXCLUDES: u32 = (VALUE | TYPE) & !REGULAR_ENUM & !VALUE_MODULE;
pub const CONST_ENUM_EXCLUDES: u32 = (VALUE | TYPE) & !CONST_ENUM & !VALUE_MODULE;
pub const VALUE_MODULE_EXCLUDES: u32 =
VALUE & !FUNCTION & !CLASS & !REGULAR_ENUM & !VALUE_MODULE;
pub const NAMESPACE_MODULE_EXCLUDES: u32 = NONE;
pub const METHOD_EXCLUDES: u32 = VALUE & !METHOD;
pub const GET_ACCESSOR_EXCLUDES: u32 = VALUE & !SET_ACCESSOR;
pub const SET_ACCESSOR_EXCLUDES: u32 = VALUE & !GET_ACCESSOR;
pub const TYPE_PARAMETER_EXCLUDES: u32 = TYPE & !TYPE_PARAMETER;
pub const TYPE_ALIAS_EXCLUDES: u32 = TYPE;
pub const ALIAS_EXCLUDES: u32 = ALIAS;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct SymbolId(pub u32);
impl SymbolId {
pub const NONE: Self = Self(u32::MAX);
#[must_use]
pub const fn is_none(&self) -> bool {
self.0 == u32::MAX
}
#[must_use]
pub const fn is_some(&self) -> bool {
self.0 != u32::MAX
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Symbol {
pub flags: u32,
pub escaped_name: String,
pub declarations: Vec<NodeIndex>,
pub value_declaration: NodeIndex,
pub parent: SymbolId,
pub id: SymbolId,
pub exports: Option<Box<SymbolTable>>,
pub members: Option<Box<SymbolTable>>,
pub is_exported: bool,
pub is_type_only: bool,
pub decl_file_idx: u32,
pub import_module: Option<String>,
pub import_name: Option<String>,
}
impl Symbol {
#[must_use]
pub const fn new(id: SymbolId, flags: u32, name: String) -> Self {
Self {
flags,
escaped_name: name,
declarations: Vec::new(),
value_declaration: NodeIndex::NONE,
parent: SymbolId::NONE,
id,
exports: None,
members: None,
is_exported: false,
is_type_only: false,
decl_file_idx: u32::MAX,
import_module: None,
import_name: None,
}
}
#[must_use]
pub const fn has_flags(&self, flags: u32) -> bool {
(self.flags & flags) == flags
}
#[must_use]
pub const fn has_any_flags(&self, flags: u32) -> bool {
(self.flags & flags) != 0
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SymbolTable {
symbols: FxHashMap<String, SymbolId>,
}
impl SymbolTable {
#[must_use]
pub fn new() -> Self {
Self {
symbols: FxHashMap::default(),
}
}
#[must_use]
pub fn get(&self, name: &str) -> Option<SymbolId> {
self.symbols.get(name).copied()
}
pub fn set(&mut self, name: String, symbol: SymbolId) {
self.symbols.insert(name, symbol);
}
pub fn remove(&mut self, name: &str) -> Option<SymbolId> {
self.symbols.remove(name)
}
#[must_use]
pub fn has(&self, name: &str) -> bool {
self.symbols.contains_key(name)
}
#[must_use]
pub fn len(&self) -> usize {
self.symbols.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.symbols.is_empty()
}
pub fn clear(&mut self) {
self.symbols.clear();
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &SymbolId)> {
self.symbols.iter()
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct SymbolArena {
symbols: Vec<Symbol>,
base_offset: u32,
}
impl SymbolArena {
pub const CHECKER_SYMBOL_BASE: u32 = 0x1000_0000;
const MAX_SYMBOL_PREALLOC: usize = 1_000_000;
#[must_use]
pub const fn new() -> Self {
Self {
symbols: Vec::new(),
base_offset: 0,
}
}
#[must_use]
pub const fn new_with_base(base: u32) -> Self {
Self {
symbols: Vec::new(),
base_offset: base,
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
let safe_capacity = capacity.min(Self::MAX_SYMBOL_PREALLOC);
Self {
symbols: Vec::with_capacity(safe_capacity),
base_offset: 0,
}
}
pub fn alloc(&mut self, flags: u32, name: String) -> SymbolId {
let id = SymbolId(
self.base_offset
+ u32::try_from(self.symbols.len()).expect("symbol arena length exceeds u32"),
);
self.symbols.push(Symbol::new(id, flags, name));
id
}
pub fn alloc_from(&mut self, source: &Symbol) -> SymbolId {
let id = SymbolId(
self.base_offset
+ u32::try_from(self.symbols.len()).expect("symbol arena length exceeds u32"),
);
let mut cloned = source.clone();
cloned.id = id;
self.symbols.push(cloned);
id
}
#[must_use]
pub fn get(&self, id: SymbolId) -> Option<&Symbol> {
if id.is_none() {
None
} else if id.0 < self.base_offset {
None
} else {
self.symbols.get((id.0 - self.base_offset) as usize)
}
}
pub fn get_mut(&mut self, id: SymbolId) -> Option<&mut Symbol> {
if id.is_none() {
None
} else if id.0 < self.base_offset {
None
} else {
self.symbols.get_mut((id.0 - self.base_offset) as usize)
}
}
#[must_use]
pub const fn len(&self) -> usize {
self.symbols.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.symbols.is_empty()
}
pub fn clear(&mut self) {
self.symbols.clear();
}
#[must_use]
pub fn find_by_name(&self, name: &str) -> Option<SymbolId> {
for symbol in &self.symbols {
if symbol.escaped_name == name {
return Some(symbol.id);
}
}
None
}
#[must_use]
pub fn find_all_by_name(&self, name: &str) -> Vec<SymbolId> {
self.symbols
.iter()
.filter(|s| s.escaped_name == name)
.map(|s| s.id)
.collect()
}
pub fn iter(&self) -> impl Iterator<Item = &Symbol> {
self.symbols.iter()
}
pub fn reserve_symbol_ids(&mut self, count: usize) {
let current_len = self.symbols.len();
if count > current_len {
self.symbols.reserve(count);
for id in current_len..count {
self.symbols.push(Symbol::new(
SymbolId(u32::try_from(id).expect("symbol ID exceeds u32")),
0,
String::new(), ));
}
}
}
}
pub mod flow_flags {
pub const UNREACHABLE: u32 = 1 << 0; pub const START: u32 = 1 << 1; pub const BRANCH_LABEL: u32 = 1 << 2; pub const LOOP_LABEL: u32 = 1 << 3; pub const ASSIGNMENT: u32 = 1 << 4; pub const TRUE_CONDITION: u32 = 1 << 5; pub const FALSE_CONDITION: u32 = 1 << 6; pub const SWITCH_CLAUSE: u32 = 1 << 7; pub const ARRAY_MUTATION: u32 = 1 << 8; pub const CALL: u32 = 1 << 9; pub const REDUCE_LABEL: u32 = 1 << 10; pub const REFERENCED: u32 = 1 << 11; pub const AWAIT_POINT: u32 = 1 << 12; pub const YIELD_POINT: u32 = 1 << 13;
pub const LABEL: u32 = BRANCH_LABEL | LOOP_LABEL;
pub const CONDITION: u32 = TRUE_CONDITION | FALSE_CONDITION;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FlowNodeId(pub u32);
impl FlowNodeId {
pub const NONE: Self = Self(u32::MAX);
#[must_use]
pub const fn is_none(&self) -> bool {
self.0 == u32::MAX
}
#[must_use]
pub const fn is_some(&self) -> bool {
self.0 != u32::MAX
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FlowNode {
pub flags: u32,
pub id: FlowNodeId,
pub antecedent: Vec<FlowNodeId>,
pub node: NodeIndex,
}
impl FlowNode {
#[must_use]
pub const fn new(id: FlowNodeId, flags: u32) -> Self {
Self {
flags,
id,
antecedent: Vec::new(),
node: NodeIndex::NONE,
}
}
#[must_use]
pub const fn has_flags(&self, flags: u32) -> bool {
(self.flags & flags) == flags
}
#[must_use]
pub const fn has_any_flags(&self, flags: u32) -> bool {
(self.flags & flags) != 0
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct FlowNodeArena {
nodes: Vec<FlowNode>,
}
impl FlowNodeArena {
#[must_use]
pub const fn new() -> Self {
Self { nodes: Vec::new() }
}
pub fn alloc(&mut self, flags: u32) -> FlowNodeId {
let id = FlowNodeId(
u32::try_from(self.nodes.len()).expect("flow node arena length exceeds u32"),
);
self.nodes.push(FlowNode::new(id, flags));
id
}
#[must_use]
pub fn get(&self, id: FlowNodeId) -> Option<&FlowNode> {
if id.is_none() {
None
} else {
self.nodes.get(id.0 as usize)
}
}
pub fn get_mut(&mut self, id: FlowNodeId) -> Option<&mut FlowNode> {
if id.is_none() {
None
} else {
self.nodes.get_mut(id.0 as usize)
}
}
#[must_use]
pub const fn len(&self) -> usize {
self.nodes.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn clear(&mut self) {
self.nodes.clear();
}
#[must_use]
pub fn find_unreachable(&self) -> Option<FlowNodeId> {
for (idx, node) in self.nodes.iter().enumerate() {
if node.has_any_flags(flow_flags::UNREACHABLE) {
return Some(FlowNodeId(
u32::try_from(idx).expect("flow node index exceeds u32"),
));
}
}
None
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ScopeId(pub u32);
impl ScopeId {
pub const NONE: Self = Self(u32::MAX);
#[must_use]
pub const fn is_none(&self) -> bool {
self.0 == u32::MAX
}
#[must_use]
pub const fn is_some(&self) -> bool {
self.0 != u32::MAX
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ContainerKind {
SourceFile,
Function,
Module,
Class,
Block,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Scope {
pub parent: ScopeId,
pub table: SymbolTable,
pub kind: ContainerKind,
pub container_node: NodeIndex,
}
impl Scope {
#[must_use]
pub fn new(parent: ScopeId, kind: ContainerKind, node: NodeIndex) -> Self {
Self {
parent,
table: SymbolTable::new(),
kind,
container_node: node,
}
}
#[must_use]
pub const fn is_function_scope(&self) -> bool {
matches!(
self.kind,
ContainerKind::SourceFile | ContainerKind::Function | ContainerKind::Module
)
}
}
#[derive(Clone, Debug)]
pub struct ScopeContext {
pub locals: SymbolTable,
pub parent_idx: Option<usize>,
pub container_kind: ContainerKind,
pub container_node: NodeIndex,
pub hoisted_vars: Vec<(String, NodeIndex)>,
pub hoisted_functions: Vec<(String, NodeIndex)>,
}
impl ScopeContext {
#[must_use]
pub fn new(kind: ContainerKind, node: NodeIndex, parent: Option<usize>) -> Self {
Self {
locals: SymbolTable::new(),
parent_idx: parent,
container_kind: kind,
container_node: node,
hoisted_vars: Vec::new(),
hoisted_functions: Vec::new(),
}
}
#[must_use]
pub const fn is_function_scope(&self) -> bool {
matches!(
self.container_kind,
ContainerKind::SourceFile | ContainerKind::Function | ContainerKind::Module
)
}
}