Skip to content

Commit

Permalink
refactor(js_semantic): separate binding and scope nodes (#3387)
Browse files Browse the repository at this point in the history
  • Loading branch information
Conaclos authored Jul 8, 2024
1 parent bc0d302 commit d9ed6d1
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 89 deletions.
2 changes: 1 addition & 1 deletion crates/biome_js_semantic/src/semantic_model/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl Binding {
/// Returns the syntax node associated with this binding.
pub fn syntax(&self) -> &JsSyntaxNode {
let binding = self.data.binding(self.index);
&self.data.node_by_range[&binding.range]
&self.data.binding_node_by_start[&binding.range.start()]
}

/// Returns the typed AST node associated with this binding.
Expand Down
53 changes: 28 additions & 25 deletions crates/biome_js_semantic/src/semantic_model/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ use std::collections::hash_map::Entry;
/// [std::sync::Arc] and stored inside the [SemanticModel].
pub struct SemanticModelBuilder {
root: AnyJsRoot,
node_by_range: FxHashMap<TextRange, JsSyntaxNode>,
binding_node_by_start: FxHashMap<TextSize, JsSyntaxNode>,
scope_node_by_range: FxHashMap<TextRange, JsSyntaxNode>,
globals: Vec<SemanticModelGlobalBindingData>,
globals_by_name: FxHashMap<String, Option<usize>>,
globals_by_name: FxHashMap<String, Option<u32>>,
scopes: Vec<SemanticModelScopeData>,
scope_range_by_start: FxHashMap<TextSize, BTreeSet<Interval<u32, u32>>>,
scope_hoisted_to_by_range: FxHashMap<TextSize, u32>,
bindings: Vec<SemanticModelBindingData>,
/// maps a binding range start to its index inside SemanticModelBuilder::bindings vec
bindings_by_start: FxHashMap<TextSize, usize>,
/// maps a reference range start to its bindings. usize points to SemanticModelBuilder::bindings vec
declared_at_by_start: FxHashMap<TextSize, usize>,
bindings_by_start: FxHashMap<TextSize, u32>,
/// maps a reference range start to its bindings. u32 points to SemanticModelBuilder::bindings vec
declared_at_by_start: FxHashMap<TextSize, u32>,
exported: FxHashSet<TextSize>,
unresolved_references: Vec<SemanticModelUnresolvedReference>,
}
Expand All @@ -30,7 +31,8 @@ impl SemanticModelBuilder {
pub fn new(root: AnyJsRoot) -> Self {
Self {
root,
node_by_range: FxHashMap::default(),
binding_node_by_start: FxHashMap::default(),
scope_node_by_range: FxHashMap::default(),
globals: vec![],
globals_by_name: FxHashMap::default(),
scopes: vec![],
Expand All @@ -56,8 +58,8 @@ impl SemanticModelBuilder {
| TS_TYPE_PARAMETER_NAME
| TS_LITERAL_ENUM_MEMBER_NAME
| JS_IDENTIFIER_ASSIGNMENT => {
self.node_by_range
.insert(node.text_trimmed_range(), node.clone());
self.binding_node_by_start
.insert(node.text_trimmed_range().start(), node.clone());
}

// Accessible from scopes, closures
Expand Down Expand Up @@ -99,14 +101,14 @@ impl SemanticModelBuilder {
| JS_CATCH_CLAUSE
| TS_FUNCTION_TYPE
| TS_MAPPED_TYPE => {
self.node_by_range
self.scope_node_by_range
.insert(node.text_trimmed_range(), node.clone());
}
_ => {
if let Some(conditional_type) = TsConditionalType::cast_ref(node) {
if let Ok(conditional_true_type) = conditional_type.true_type() {
let syntax = conditional_true_type.into_syntax();
self.node_by_range
self.scope_node_by_range
.insert(syntax.text_trimmed_range(), syntax);
}
}
Expand Down Expand Up @@ -171,7 +173,7 @@ impl SemanticModelBuilder {
// event extractor
debug_assert!((binding_scope_id as usize) < self.scopes.len());

let binding_id = self.bindings.len();
let binding_id = self.bindings.len() as u32;
self.bindings.push(SemanticModelBindingData {
id: binding_id.into(),
range,
Expand All @@ -183,7 +185,7 @@ impl SemanticModelBuilder {

scope.bindings.push(binding_id);
// Handle bindings with a bogus name
if let Some(node) = self.node_by_range.get(&range) {
if let Some(node) = self.binding_node_by_start.get(&range.start()) {
if let Some(node) = JsIdentifierBinding::cast_ref(node) {
if let Ok(name_token) = node.name_token() {
let name = name_token.token_text_trimmed();
Expand All @@ -205,7 +207,7 @@ impl SemanticModelBuilder {
let binding_id = match self.bindings_by_start.entry(declaration_at.start()) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let id = self.bindings.len();
let id = self.bindings.len() as u32;
self.bindings.push(SemanticModelBindingData {
id: id.into(),
range,
Expand All @@ -214,8 +216,8 @@ impl SemanticModelBuilder {
*entry.insert(id)
}
};
let binding = &mut self.bindings[binding_id];
let reference_index = binding.references.len();
let binding = &mut self.bindings[binding_id as usize];
let reference_index = binding.references.len() as u32;

binding.references.push(SemanticModelReference {
index: (binding.id, reference_index).into(),
Expand All @@ -237,9 +239,9 @@ impl SemanticModelBuilder {
scope_id,
} => {
let binding_id = self.bindings_by_start[&declaration_at.start()];
let binding = &mut self.bindings[binding_id];
let binding = &mut self.bindings[binding_id as usize];

let reference_index = binding.references.len();
let reference_index = binding.references.len() as u32;
binding.references.push(SemanticModelReference {
index: (binding.id, reference_index).into(),
range,
Expand All @@ -260,9 +262,9 @@ impl SemanticModelBuilder {
scope_id,
} => {
let binding_id = self.bindings_by_start[&declaration_at.start()];
let binding = &mut self.bindings[binding_id];
let binding = &mut self.bindings[binding_id as usize];

let reference_index = binding.references.len();
let reference_index = binding.references.len() as u32;
binding.references.push(SemanticModelReference {
index: (binding.id, reference_index).into(),
range,
Expand All @@ -283,9 +285,9 @@ impl SemanticModelBuilder {
scope_id,
} => {
let binding_id = self.bindings_by_start[&declaration_at.start()];
let binding = &mut self.bindings[binding_id];
let binding = &mut self.bindings[binding_id as usize];

let reference_index = binding.references.len();
let reference_index = binding.references.len() as u32;
binding.references.push(SemanticModelReference {
index: (binding.id, reference_index).into(),
range,
Expand All @@ -307,20 +309,20 @@ impl SemanticModelBuilder {
SemanticModelReferenceType::Write { hoisted: false }
};

let node = &self.node_by_range[&range];
let node = &self.binding_node_by_start[&range.start()];
let name = node.text_trimmed().to_string();

match self.globals_by_name.entry(name) {
Entry::Occupied(mut entry) => {
let entry = entry.get_mut();
match entry {
Some(index) => {
self.globals[*index]
self.globals[(*index) as usize]
.references
.push(SemanticModelGlobalReferenceData { range, ty });
}
None => {
let id = self.globals.len();
let id = self.globals.len() as u32;
self.globals.push(SemanticModelGlobalBindingData {
references: vec![SemanticModelGlobalReferenceData {
range,
Expand Down Expand Up @@ -355,7 +357,8 @@ impl SemanticModelBuilder {
.collect(),
),
scope_hoisted_to_by_range: self.scope_hoisted_to_by_range,
node_by_range: self.node_by_range,
binding_node_by_start: self.binding_node_by_start,
scope_node_by_range: self.scope_node_by_range,
bindings: self.bindings,
bindings_by_start: self.bindings_by_start,
declared_at_by_start: self.declared_at_by_start,
Expand Down
33 changes: 9 additions & 24 deletions crates/biome_js_semantic/src/semantic_model/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ impl Iterator for AllCapturesIter {
fn next(&mut self) -> Option<Self::Item> {
'references: loop {
while let Some(reference) = self.references.pop() {
let binding = &self.data.bindings[reference.binding_id];
let binding = &self.data.bindings[reference.binding_id as usize];
if self.closure_range.intersect(binding.range).is_none() {
let reference = &binding.references[reference.reference_id];
let reference = &binding.references[reference.reference_id as usize];
return Some(Capture {
data: self.data.clone(),
node: self.data.node_by_range[&reference.range].clone(), // TODO change node to store the range
node: self.data.binding_node_by_start[&reference.range.start()].clone(), // TODO change node to store the range
ty: CaptureType::ByReference,
binding_id: binding.id,
});
Expand Down Expand Up @@ -180,7 +180,6 @@ impl Iterator for ChildrenIter {
return Some(Closure {
data: self.data.clone(),
scope_id,
closure_range: scope.range,
});
} else {
self.scopes.extend(scope.children.iter());
Expand Down Expand Up @@ -210,7 +209,6 @@ impl Iterator for DescendentsIter {
return Some(Closure {
data: self.data.clone(),
scope_id,
closure_range: scope.range,
});
}
}
Expand All @@ -226,42 +224,29 @@ impl FusedIterator for DescendentsIter {}
pub struct Closure {
data: Rc<SemanticModelData>,
scope_id: u32,
closure_range: TextRange,
}

impl Closure {
pub(super) fn from_node(data: Rc<SemanticModelData>, node: &impl HasClosureAstNode) -> Closure {
let closure_range = node.node_text_range();
let scope_id = data.scope(&closure_range);

Closure {
data,
scope_id,
closure_range,
}
Closure { data, scope_id }
}

pub(super) fn from_scope(
data: Rc<SemanticModelData>,
scope_id: u32,
closure_range: &TextRange,
) -> Option<Closure> {
let node = &data.node_by_range[closure_range];
pub(super) fn from_scope(data: Rc<SemanticModelData>, scope_id: u32) -> Option<Closure> {
let node = &data.scope_node_by_range[&data.scopes[scope_id as usize].range];
match node.kind() {
JsSyntaxKind::JS_FUNCTION_DECLARATION
| JsSyntaxKind::JS_FUNCTION_EXPRESSION
| JsSyntaxKind::JS_ARROW_FUNCTION_EXPRESSION => Some(Closure {
data,
scope_id,
closure_range: *closure_range,
}),
| JsSyntaxKind::JS_ARROW_FUNCTION_EXPRESSION => Some(Closure { data, scope_id }),
_ => None,
}
}

/// Range of this [Closure]
pub fn closure_range(&self) -> &TextRange {
&self.closure_range
&self.data.scopes[self.scope_id as usize].range
}

/// Return all [Reference] this closure captures, not taking into
Expand All @@ -287,7 +272,7 @@ impl Closure {

AllCapturesIter {
data: self.data.clone(),
closure_range: self.closure_range,
closure_range: *self.closure_range(),
scopes,
references,
}
Expand Down
12 changes: 6 additions & 6 deletions crates/biome_js_semantic/src/semantic_model/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,25 @@ pub struct SemanticModelGlobalReferenceData {

pub struct GlobalReference {
pub(crate) data: Rc<SemanticModelData>,
pub(crate) global_id: usize,
pub(crate) id: usize,
pub(crate) global_id: u32,
pub(crate) id: u32,
}

impl GlobalReference {
pub fn syntax(&self) -> &JsSyntaxNode {
let reference = &self.data.globals[self.global_id].references[self.id];
&self.data.node_by_range[&reference.range]
let reference = &self.data.globals[self.global_id as usize].references[self.id as usize];
&self.data.binding_node_by_start[&reference.range.start()]
}

/// Returns if this reference is just reading its binding
pub fn is_read(&self) -> bool {
let reference = &self.data.globals[self.global_id].references[self.id];
let reference = &self.data.globals[self.global_id as usize].references[self.id as usize];
matches!(reference.ty, SemanticModelReferenceType::Read { .. })
}

/// Returns if this reference is writing its binding
pub fn is_write(&self) -> bool {
let reference = &self.data.globals[self.global_id].references[self.id];
let reference = &self.data.globals[self.global_id as usize].references[self.id as usize];
matches!(reference.ty, SemanticModelReferenceType::Write { .. })
}
}
Loading

0 comments on commit d9ed6d1

Please sign in to comment.