mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
refactor(cfg)!: move control flow to its own crate. (#3728)
This commit is contained in:
parent
5c38a0fd69
commit
0537d298db
22 changed files with 244 additions and 184 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
|
@ -1348,6 +1348,17 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxc_cfg"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"itertools 0.13.0",
|
||||
"oxc_syntax",
|
||||
"petgraph",
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxc_codegen"
|
||||
version = "0.14.0"
|
||||
|
|
@ -1478,6 +1489,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
"oxc_cfg",
|
||||
"oxc_codegen",
|
||||
"oxc_diagnostics",
|
||||
"oxc_macros",
|
||||
|
|
@ -1652,18 +1664,17 @@ dependencies = [
|
|||
name = "oxc_semantic"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"indexmap",
|
||||
"insta",
|
||||
"itertools 0.13.0",
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
"oxc_cfg",
|
||||
"oxc_diagnostics",
|
||||
"oxc_index",
|
||||
"oxc_parser",
|
||||
"oxc_span",
|
||||
"oxc_syntax",
|
||||
"petgraph",
|
||||
"phf",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ oxc_module_lexer = { version = "0.14.0", path = "crates/oxc_module_lexe
|
|||
oxc_isolated_declarations = { version = "0.14.0", path = "crates/oxc_isolated_declarations" }
|
||||
|
||||
# publish = false
|
||||
oxc_cfg = { path = "crates/oxc_cfg" }
|
||||
oxc_macros = { path = "crates/oxc_macros" }
|
||||
oxc_linter = { path = "crates/oxc_linter" }
|
||||
oxc_prettier = { path = "crates/oxc_prettier" }
|
||||
|
|
|
|||
27
crates/oxc_cfg/Cargo.toml
Normal file
27
crates/oxc_cfg/Cargo.toml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
[package]
|
||||
name = "oxc_cfg"
|
||||
version = "0.0.0"
|
||||
authors.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
categories.workspace = true
|
||||
include = ["/src"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = true
|
||||
|
||||
[dependencies]
|
||||
oxc_syntax = { workspace = true }
|
||||
|
||||
itertools = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
petgraph = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
|
|
@ -40,6 +40,7 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
|
||||
pub trait CtxCursor {
|
||||
#![allow(clippy::return_self_not_must_use)]
|
||||
/// Marks the break jump position in the current context.
|
||||
fn mark_break(self, jmp_pos: BasicBlockId) -> Self;
|
||||
/// Marks the continue jump position in the current context.
|
||||
|
|
@ -4,11 +4,12 @@ use crate::ReturnInstructionKind;
|
|||
use context::Ctx;
|
||||
|
||||
pub use context::{CtxCursor, CtxFlags};
|
||||
use oxc_syntax::node::AstNodeId;
|
||||
use petgraph::Direction;
|
||||
|
||||
use super::{
|
||||
AstNodeId, BasicBlock, BasicBlockId, ControlFlowGraph, EdgeType, ErrorEdgeKind, Graph,
|
||||
Instruction, InstructionKind, IterationInstructionKind, LabeledInstruction,
|
||||
BasicBlock, BasicBlockId, ControlFlowGraph, EdgeType, ErrorEdgeKind, Graph, Instruction,
|
||||
InstructionKind, IterationInstructionKind, LabeledInstruction,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -70,7 +71,8 @@ impl<'a> ControlFlowGraphBuilder<'a> {
|
|||
self.new_basic_block_normal()
|
||||
}
|
||||
|
||||
/// # Panics if there is no error harness to attach to.
|
||||
/// # Panics
|
||||
/// if there is no error harness to attach to.
|
||||
#[must_use]
|
||||
pub fn new_basic_block_normal(&mut self) -> BasicBlockId {
|
||||
let graph_ix = self.new_basic_block();
|
||||
|
|
@ -122,7 +124,8 @@ impl<'a> ControlFlowGraphBuilder<'a> {
|
|||
graph_ix
|
||||
}
|
||||
|
||||
/// # Panics if there is no error harness pushed onto the stack,
|
||||
/// # Panics
|
||||
/// if there is no error harness pushed onto the stack,
|
||||
/// Or last harness doesn't match the expected `BasicBlockId`.
|
||||
pub fn release_error_harness(&mut self, expect: BasicBlockId) {
|
||||
let harness = self
|
||||
|
|
@ -152,7 +155,8 @@ impl<'a> ControlFlowGraphBuilder<'a> {
|
|||
debug_assert!(result.as_ref().is_some_and(Option::is_none));
|
||||
}
|
||||
|
||||
/// # Panics if last finalizer doesn't match the expected `BasicBlockId`.
|
||||
/// # Panics
|
||||
/// if last finalizer doesn't match the expected `BasicBlockId`.
|
||||
pub fn release_finalizer(&mut self, expect: BasicBlockId) {
|
||||
// return early if there is no finalizer.
|
||||
let Some(finalizer) = self.finalizers.pop() else { return };
|
||||
|
|
@ -216,6 +220,7 @@ impl<'a> ControlFlowGraphBuilder<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
#[inline]
|
||||
pub(self) fn push_instruction(&mut self, kind: InstructionKind, node_id: Option<AstNodeId>) {
|
||||
self.push_instruction_to(self.current_node_ix, kind, node_id);
|
||||
80
crates/oxc_cfg/src/dot.rs
Normal file
80
crates/oxc_cfg/src/dot.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// use oxc_ast::{
|
||||
// ast::{BreakStatement, ContinueStatement},
|
||||
// AstKind,
|
||||
// };
|
||||
use petgraph::{
|
||||
dot::{Config, Dot},
|
||||
visit::EdgeRef,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
BasicBlock, ControlFlowGraph, EdgeType, Instruction, InstructionKind, LabeledInstruction,
|
||||
ReturnInstructionKind,
|
||||
};
|
||||
|
||||
use super::IterationInstructionKind;
|
||||
|
||||
pub trait DisplayDot {
|
||||
fn display_dot(&self) -> String;
|
||||
}
|
||||
|
||||
impl DisplayDot for ControlFlowGraph {
|
||||
fn display_dot(&self) -> String {
|
||||
format!(
|
||||
"{:?}",
|
||||
Dot::with_attr_getters(
|
||||
&self.graph,
|
||||
&[Config::EdgeNoLabel, Config::NodeNoLabel],
|
||||
&|_graph, edge| {
|
||||
let weight = edge.weight();
|
||||
let label = format!("label = \"{weight:?}\" ");
|
||||
if matches!(weight, EdgeType::Unreachable)
|
||||
|| self.basic_block(edge.source()).unreachable
|
||||
{
|
||||
format!("{label}, style = \"dotted\" ")
|
||||
} else {
|
||||
label
|
||||
}
|
||||
},
|
||||
&|_graph, node| format!(
|
||||
"label = {:?} ",
|
||||
self.basic_blocks[*node.1].display_dot().trim()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayDot for BasicBlock {
|
||||
fn display_dot(&self) -> String {
|
||||
self.instructions().iter().fold(String::new(), |mut acc, it| {
|
||||
acc.push_str(it.display_dot().as_str());
|
||||
acc.push('\n');
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayDot for Instruction {
|
||||
fn display_dot(&self) -> String {
|
||||
match self.kind {
|
||||
InstructionKind::Statement => "statement",
|
||||
InstructionKind::Unreachable => "unreachable",
|
||||
InstructionKind::Throw => "throw",
|
||||
InstructionKind::Condition => "condition",
|
||||
InstructionKind::Iteration(IterationInstructionKind::Of) => "iteration <of>",
|
||||
InstructionKind::Iteration(IterationInstructionKind::In) => "iteration <in>",
|
||||
InstructionKind::Break(LabeledInstruction::Labeled) => "break <label>",
|
||||
InstructionKind::Break(LabeledInstruction::Unlabeled) => "break",
|
||||
InstructionKind::Continue(LabeledInstruction::Labeled) => "continue <label>",
|
||||
InstructionKind::Continue(LabeledInstruction::Unlabeled) => "continue",
|
||||
InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) => {
|
||||
"return <implicit undefined>"
|
||||
}
|
||||
InstructionKind::Return(ReturnInstructionKind::NotImplicitUndefined) => {
|
||||
"return <value>"
|
||||
}
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ mod dot;
|
|||
pub mod visit;
|
||||
|
||||
use itertools::Itertools;
|
||||
use oxc_ast::AstKind;
|
||||
use oxc_syntax::node::AstNodeId;
|
||||
use petgraph::{
|
||||
stable_graph::NodeIndex,
|
||||
visit::{depth_first_search, Control, DfsEvent, EdgeRef},
|
||||
|
|
@ -18,10 +18,8 @@ pub mod graph {
|
|||
}
|
||||
}
|
||||
|
||||
use crate::{AstNodeId, AstNodes};
|
||||
|
||||
pub(crate) use builder::{ControlFlowGraphBuilder, CtxCursor, CtxFlags};
|
||||
pub use dot::{DebugDot, DebugDotContext, DisplayDot};
|
||||
pub use builder::{ControlFlowGraphBuilder, CtxCursor, CtxFlags};
|
||||
pub use dot::DisplayDot;
|
||||
|
||||
pub type BasicBlockId = NodeIndex;
|
||||
|
||||
|
|
@ -113,6 +111,12 @@ pub enum ErrorEdgeKind {
|
|||
Implicit,
|
||||
}
|
||||
|
||||
pub enum EvalConstConditionResult {
|
||||
NotFound,
|
||||
Fail,
|
||||
Eval(bool),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ControlFlowGraph {
|
||||
pub graph: Graph<usize, EdgeType>,
|
||||
|
|
@ -172,32 +176,14 @@ impl ControlFlowGraph {
|
|||
|
||||
/// Returns `None` the given node isn't the cyclic point of an infinite loop.
|
||||
/// Otherwise returns `Some(loop_start, loop_end)`.
|
||||
pub fn is_infinite_loop_start(
|
||||
pub fn is_infinite_loop_start<F>(
|
||||
&self,
|
||||
node: BasicBlockId,
|
||||
nodes: &AstNodes,
|
||||
) -> Option<(BasicBlockId, BasicBlockId)> {
|
||||
enum EvalConstConditionResult {
|
||||
NotFound,
|
||||
Fail,
|
||||
Eval(bool),
|
||||
}
|
||||
fn try_eval_const_condition(
|
||||
instruction: &Instruction,
|
||||
nodes: &AstNodes,
|
||||
) -> EvalConstConditionResult {
|
||||
use EvalConstConditionResult::{Eval, Fail, NotFound};
|
||||
match instruction {
|
||||
Instruction { kind: InstructionKind::Condition, node_id: Some(id) } => {
|
||||
match nodes.kind(*id) {
|
||||
AstKind::BooleanLiteral(lit) => Eval(lit.value),
|
||||
_ => Fail,
|
||||
}
|
||||
}
|
||||
_ => NotFound,
|
||||
}
|
||||
}
|
||||
|
||||
try_eval_const_condition: F,
|
||||
) -> Option<(BasicBlockId, BasicBlockId)>
|
||||
where
|
||||
F: Fn(&Instruction) -> EvalConstConditionResult,
|
||||
{
|
||||
fn get_jump_target(
|
||||
graph: &Graph<usize, EdgeType>,
|
||||
node: BasicBlockId,
|
||||
|
|
@ -239,15 +225,14 @@ impl ControlFlowGraph {
|
|||
|
||||
// if there is exactly one and it is a condition instruction we are in a loop so we
|
||||
// check the condition to infer if it is always true.
|
||||
if let EvalConstConditionResult::Eval(true) =
|
||||
try_eval_const_condition(only_instruction, nodes)
|
||||
{
|
||||
if let EvalConstConditionResult::Eval(true) = try_eval_const_condition(only_instruction) {
|
||||
get_jump_target(&self.graph, node).map(|it| (it, node))
|
||||
} else if let EvalConstConditionResult::Eval(true) =
|
||||
self.basic_block(backedge.source()).instructions().iter().exactly_one().map_or_else(
|
||||
|_| EvalConstConditionResult::NotFound,
|
||||
|it| try_eval_const_condition(it, nodes),
|
||||
)
|
||||
} else if let EvalConstConditionResult::Eval(true) = self
|
||||
.basic_block(backedge.source())
|
||||
.instructions()
|
||||
.iter()
|
||||
.exactly_one()
|
||||
.map_or_else(|_| EvalConstConditionResult::NotFound, try_eval_const_condition)
|
||||
{
|
||||
get_jump_target(&self.graph, node).map(|it| (node, it))
|
||||
} else {
|
||||
|
|
@ -20,16 +20,17 @@ workspace = true
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
oxc_allocator = { workspace = true }
|
||||
oxc_parser = { workspace = true }
|
||||
oxc_span = { workspace = true }
|
||||
oxc_ast = { workspace = true }
|
||||
oxc_diagnostics = { workspace = true }
|
||||
oxc_macros = { workspace = true }
|
||||
oxc_semantic = { workspace = true }
|
||||
oxc_syntax = { workspace = true }
|
||||
oxc_codegen = { workspace = true }
|
||||
oxc_resolver = { workspace = true }
|
||||
oxc_allocator = { workspace = true }
|
||||
oxc_parser = { workspace = true }
|
||||
oxc_span = { workspace = true }
|
||||
oxc_ast = { workspace = true }
|
||||
oxc_cfg = { workspace = true }
|
||||
oxc_diagnostics = { workspace = true }
|
||||
oxc_macros = { workspace = true }
|
||||
oxc_semantic = { workspace = true }
|
||||
oxc_syntax = { workspace = true }
|
||||
oxc_codegen = { workspace = true }
|
||||
oxc_resolver = { workspace = true }
|
||||
|
||||
rayon = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
|
|
@ -51,6 +52,6 @@ json-strip-comments = { workspace = true }
|
|||
schemars = { workspace = true, features = ["indexmap2"] }
|
||||
|
||||
[dev-dependencies]
|
||||
static_assertions = { workspace = true }
|
||||
insta = { workspace = true }
|
||||
project-root = { workspace = true }
|
||||
static_assertions = { workspace = true }
|
||||
insta = { workspace = true }
|
||||
project-root = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ use oxc_ast::{
|
|||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::{
|
||||
control_flow::graph::visit::neighbors_filtered_by_edge_weight, EdgeType, InstructionKind,
|
||||
use oxc_cfg::{
|
||||
graph::visit::neighbors_filtered_by_edge_weight, EdgeType, InstructionKind,
|
||||
ReturnInstructionKind,
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@ use oxc_ast::{
|
|||
ast::{Statement, SwitchCase, SwitchStatement},
|
||||
AstKind,
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::{
|
||||
control_flow::graph::{
|
||||
use oxc_cfg::{
|
||||
graph::{
|
||||
visit::{neighbors_filtered_by_edge_weight, EdgeRef},
|
||||
Direction,
|
||||
},
|
||||
BasicBlockId, EdgeType, ErrorEdgeKind, InstructionKind,
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use regex::Regex;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_semantic::AstNodeId;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
|
|
@ -6,11 +7,11 @@ use oxc_ast::{
|
|||
ast::{Expression, MethodDefinitionKind},
|
||||
AstKind,
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::{
|
||||
control_flow::graph::visit::{neighbors_filtered_by_edge_weight, EdgeRef},
|
||||
AstNodeId, BasicBlockId, ControlFlowGraph, EdgeType,
|
||||
use oxc_cfg::{
|
||||
graph::visit::{neighbors_filtered_by_edge_weight, EdgeRef},
|
||||
BasicBlockId, ControlFlowGraph, EdgeType, ErrorEdgeKind,
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
|
|
@ -187,8 +188,7 @@ impl NoThisBeforeSuper {
|
|||
if cfg.graph.edges(*basic_block_id).any(|it| {
|
||||
matches!(
|
||||
it.weight(),
|
||||
EdgeType::Error(oxc_semantic::ErrorEdgeKind::Explicit)
|
||||
| EdgeType::Finalize
|
||||
EdgeType::Error(ErrorEdgeKind::Explicit) | EdgeType::Finalize
|
||||
)
|
||||
}) {
|
||||
(DefinitelyCallsThisBeforeSuper::Maybe(*basic_block_id), false)
|
||||
|
|
@ -227,8 +227,7 @@ impl NoThisBeforeSuper {
|
|||
DefinitelyCallsThisBeforeSuper::No => false,
|
||||
DefinitelyCallsThisBeforeSuper::Maybe(id) => cfg.graph.edges(id).any(|edge| {
|
||||
let weight = edge.weight();
|
||||
let is_explicit_error =
|
||||
matches!(weight, EdgeType::Error(oxc_semantic::ErrorEdgeKind::Explicit));
|
||||
let is_explicit_error = matches!(weight, EdgeType::Error(ErrorEdgeKind::Explicit));
|
||||
if is_explicit_error || matches!(weight, EdgeType::Finalize) {
|
||||
Self::check_for_violation(
|
||||
cfg,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use oxc_ast::{ast::VariableDeclarationKind, AstKind};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::{
|
||||
control_flow::graph::{
|
||||
use oxc_cfg::{
|
||||
graph::{
|
||||
visit::{depth_first_search, Control, DfsEvent, EdgeRef},
|
||||
Direction,
|
||||
},
|
||||
EdgeType, ErrorEdgeKind, InstructionKind,
|
||||
EdgeType, ErrorEdgeKind, Instruction, InstructionKind,
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
|
|
@ -56,7 +56,18 @@ impl Rule for NoUnreachable {
|
|||
unreachables[node.index()] = unreachable;
|
||||
|
||||
if !unreachable {
|
||||
if let Some(it) = cfg.is_infinite_loop_start(node, nodes) {
|
||||
if let Some(it) = cfg.is_infinite_loop_start(node, |instruction| {
|
||||
use oxc_cfg::EvalConstConditionResult::{Eval, Fail, NotFound};
|
||||
match instruction {
|
||||
Instruction { kind: InstructionKind::Condition, node_id: Some(id) } => {
|
||||
match nodes.kind(*id) {
|
||||
AstKind::BooleanLiteral(lit) => Eval(lit.value),
|
||||
_ => Fail,
|
||||
}
|
||||
}
|
||||
_ => NotFound,
|
||||
}
|
||||
}) {
|
||||
infinite_loops.push(it);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use oxc_ast::{ast::Expression, AstKind};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::{
|
||||
control_flow::graph::visit::neighbors_filtered_by_edge_weight, EdgeType, Instruction,
|
||||
InstructionKind, ReturnInstructionKind,
|
||||
use oxc_cfg::{
|
||||
graph::visit::neighbors_filtered_by_edge_weight, EdgeType, Instruction, InstructionKind,
|
||||
ReturnInstructionKind,
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ use oxc_ast::{
|
|||
ast::{ArrowFunctionExpression, Function},
|
||||
AstKind,
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::{
|
||||
control_flow::graph::{algo, visit::Control},
|
||||
AstNodeId, AstNodes, EdgeType, ErrorEdgeKind, InstructionKind,
|
||||
use oxc_cfg::{
|
||||
graph::{algo, visit::Control},
|
||||
EdgeType, ErrorEdgeKind, InstructionKind,
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::{AstNodeId, AstNodes};
|
||||
use oxc_span::{Atom, CompactStr};
|
||||
use oxc_syntax::operator::AssignmentOperator;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,20 +19,19 @@ workspace = true
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
oxc_span = { workspace = true }
|
||||
oxc_ast = { workspace = true }
|
||||
oxc_syntax = { workspace = true }
|
||||
oxc_diagnostics = { workspace = true }
|
||||
oxc_index = { workspace = true }
|
||||
oxc_allocator = { workspace = true }
|
||||
oxc_span = { workspace = true }
|
||||
oxc_ast = { workspace = true }
|
||||
oxc_syntax = { workspace = true }
|
||||
oxc_cfg = { workspace = true }
|
||||
oxc_diagnostics = { workspace = true }
|
||||
oxc_index = { workspace = true }
|
||||
oxc_allocator = { workspace = true }
|
||||
|
||||
indexmap = { workspace = true }
|
||||
phf = { workspace = true, features = ["macros"] }
|
||||
rustc-hash = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"], optional = true }
|
||||
petgraph = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
|
||||
tsify = { workspace = true, optional = true }
|
||||
wasm-bindgen = { workspace = true, optional = true }
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@ use std::{collections::HashMap, env, path::Path, sync::Arc};
|
|||
|
||||
use itertools::Itertools;
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::{DebugDot, DisplayDot, EdgeType, SemanticBuilder};
|
||||
use oxc_span::SourceType;
|
||||
use petgraph::{
|
||||
dot::{Config, Dot},
|
||||
visit::EdgeRef,
|
||||
use oxc_cfg::{
|
||||
graph::{
|
||||
dot::{Config, Dot},
|
||||
visit::EdgeRef,
|
||||
},
|
||||
DisplayDot, EdgeType,
|
||||
};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::{dot::DebugDot, SemanticBuilder};
|
||||
use oxc_span::SourceType;
|
||||
|
||||
// Instruction:
|
||||
// 1. create a `test.js`,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ use std::{cell::RefCell, path::PathBuf, sync::Arc};
|
|||
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_ast::{ast::*, AstKind, Trivias, Visit};
|
||||
use oxc_cfg::{
|
||||
ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind,
|
||||
IterationInstructionKind, ReturnInstructionKind,
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_span::{CompactStr, SourceType, Span};
|
||||
use oxc_syntax::{
|
||||
|
|
@ -16,10 +20,6 @@ use crate::{
|
|||
binder::Binder,
|
||||
checker,
|
||||
class::ClassTableBuilder,
|
||||
control_flow::{
|
||||
ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind,
|
||||
IterationInstructionKind, ReturnInstructionKind,
|
||||
},
|
||||
diagnostics::redeclaration,
|
||||
jsdoc::JSDocBuilder,
|
||||
label::LabelBuilder,
|
||||
|
|
|
|||
|
|
@ -2,18 +2,17 @@ use oxc_ast::{
|
|||
ast::{BreakStatement, ContinueStatement},
|
||||
AstKind,
|
||||
};
|
||||
use oxc_syntax::node::AstNodeId;
|
||||
use petgraph::{
|
||||
dot::{Config, Dot},
|
||||
visit::EdgeRef,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
AstNode, AstNodes, BasicBlock, ControlFlowGraph, EdgeType, Instruction, InstructionKind,
|
||||
use oxc_cfg::{
|
||||
graph::{
|
||||
dot::{Config, Dot},
|
||||
visit::EdgeRef,
|
||||
},
|
||||
BasicBlock, ControlFlowGraph, EdgeType, Instruction, InstructionKind, IterationInstructionKind,
|
||||
LabeledInstruction, ReturnInstructionKind,
|
||||
};
|
||||
use oxc_syntax::node::AstNodeId;
|
||||
|
||||
use super::IterationInstructionKind;
|
||||
use crate::{AstNode, AstNodes};
|
||||
|
||||
pub trait DisplayDot {
|
||||
fn display_dot(&self) -> String;
|
||||
|
|
@ -49,67 +48,6 @@ impl<'a, 'b> From<&'b AstNodes<'a>> for DebugDotContext<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
impl DisplayDot for ControlFlowGraph {
|
||||
fn display_dot(&self) -> String {
|
||||
format!(
|
||||
"{:?}",
|
||||
Dot::with_attr_getters(
|
||||
&self.graph,
|
||||
&[Config::EdgeNoLabel, Config::NodeNoLabel],
|
||||
&|_graph, edge| {
|
||||
let weight = edge.weight();
|
||||
let label = format!("label = \"{weight:?}\" ");
|
||||
if matches!(weight, EdgeType::Unreachable)
|
||||
|| self.basic_block(edge.source()).unreachable
|
||||
{
|
||||
format!("{label}, style = \"dotted\" ")
|
||||
} else {
|
||||
label
|
||||
}
|
||||
},
|
||||
&|_graph, node| format!(
|
||||
"label = {:?} ",
|
||||
self.basic_blocks[*node.1].display_dot().trim()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayDot for BasicBlock {
|
||||
fn display_dot(&self) -> String {
|
||||
self.instructions().iter().fold(String::new(), |mut acc, it| {
|
||||
acc.push_str(it.display_dot().as_str());
|
||||
acc.push('\n');
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayDot for Instruction {
|
||||
fn display_dot(&self) -> String {
|
||||
match self.kind {
|
||||
InstructionKind::Statement => "statement",
|
||||
InstructionKind::Unreachable => "unreachable",
|
||||
InstructionKind::Throw => "throw",
|
||||
InstructionKind::Condition => "condition",
|
||||
InstructionKind::Iteration(IterationInstructionKind::Of) => "iteration <of>",
|
||||
InstructionKind::Iteration(IterationInstructionKind::In) => "iteration <in>",
|
||||
InstructionKind::Break(LabeledInstruction::Labeled) => "break <label>",
|
||||
InstructionKind::Break(LabeledInstruction::Unlabeled) => "break",
|
||||
InstructionKind::Continue(LabeledInstruction::Labeled) => "continue <label>",
|
||||
InstructionKind::Continue(LabeledInstruction::Unlabeled) => "continue",
|
||||
InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) => {
|
||||
"return <implicit undefined>"
|
||||
}
|
||||
InstructionKind::Return(ReturnInstructionKind::NotImplicitUndefined) => {
|
||||
"return <value>"
|
||||
}
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugDot for ControlFlowGraph {
|
||||
fn debug_dot(&self, ctx: DebugDotContext) -> String {
|
||||
format!(
|
||||
|
|
@ -166,7 +104,7 @@ impl DebugDot for Instruction {
|
|||
"Iteration({} {} {})",
|
||||
self.node_id.map_or("None".to_string(), |id| ctx.debug_ast_kind(id)),
|
||||
if matches!(kind, IterationInstructionKind::Of) { "of" } else { "in" },
|
||||
// TODO: at this point we can't evaluate this note. needs access to the graph information.
|
||||
// TODO: at this point we can't evaluate this node. needs access to the graph information.
|
||||
"expr"
|
||||
)
|
||||
}
|
||||
|
|
@ -11,14 +11,16 @@ mod reference;
|
|||
mod scope;
|
||||
mod symbol;
|
||||
|
||||
pub mod control_flow;
|
||||
pub mod dot;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use builder::{SemanticBuilder, SemanticBuilderReturn};
|
||||
use class::ClassTable;
|
||||
pub use jsdoc::{JSDoc, JSDocFinder, JSDocTag};
|
||||
pub use node::{AstNode, AstNodeId, AstNodes};
|
||||
use oxc_ast::{ast::IdentifierReference, AstKind, Trivias};
|
||||
use oxc_cfg::ControlFlowGraph;
|
||||
use oxc_span::SourceType;
|
||||
pub use oxc_syntax::{
|
||||
module_record::ModuleRecord,
|
||||
|
|
@ -28,12 +30,6 @@ pub use oxc_syntax::{
|
|||
use rustc_hash::FxHashSet;
|
||||
|
||||
pub use crate::{
|
||||
control_flow::{
|
||||
BasicBlock, BasicBlockId, ControlFlowGraph, DebugDot, DebugDotContext, DisplayDot,
|
||||
EdgeType, ErrorEdgeKind, Instruction, InstructionKind, LabeledInstruction,
|
||||
ReturnInstructionKind,
|
||||
},
|
||||
node::{AstNode, AstNodeId, AstNodes},
|
||||
reference::{Reference, ReferenceFlag, ReferenceId},
|
||||
scope::ScopeTree,
|
||||
symbol::SymbolTable,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use oxc_ast::AstKind;
|
||||
use oxc_index::IndexVec;
|
||||
|
||||
use crate::{control_flow::BasicBlockId, scope::ScopeId};
|
||||
use crate::scope::ScopeId;
|
||||
|
||||
use oxc_cfg::BasicBlockId;
|
||||
pub use oxc_syntax::node::{AstNodeId, NodeFlags};
|
||||
|
||||
/// Semantic node contains all the semantic information about an ast node.
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ use std::{path::PathBuf, sync::Arc};
|
|||
|
||||
use itertools::Itertools;
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_cfg::DisplayDot;
|
||||
use oxc_diagnostics::{Error, NamedSource, OxcDiagnostic};
|
||||
use oxc_semantic::{DebugDot, DisplayDot, Semantic, SemanticBuilder};
|
||||
use oxc_semantic::{dot::DebugDot, Semantic, SemanticBuilder};
|
||||
use oxc_span::SourceType;
|
||||
|
||||
pub use class_tester::ClassTester;
|
||||
|
|
|
|||
Loading…
Reference in a new issue