mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(semantic): track cfg index per ast node (#2210)
This allows looking up a cfg index from an ast node in a semantics return. This allows later passes to better make use of the cfg.
This commit is contained in:
parent
6b2150f3b3
commit
e561457683
33 changed files with 318 additions and 352 deletions
|
|
@ -154,7 +154,7 @@ impl GetterReturn {
|
|||
|
||||
let output = neighbors_filtered_by_edge_weight(
|
||||
&cfg.graph,
|
||||
cfg.function_to_node_ix[&node.id()],
|
||||
node.cfg_ix(),
|
||||
&|edge| match edge {
|
||||
EdgeType::Normal => None,
|
||||
// We don't need to handle backedges because we would have already visited
|
||||
|
|
@ -178,7 +178,7 @@ impl GetterReturn {
|
|||
// previous such as [`no_this_before_super`] we would want to observe this value.
|
||||
&mut |basic_block_id, _state_going_into_this_rule| {
|
||||
// Scan through the values in this basic block.
|
||||
for entry in &cfg.basic_blocks[*basic_block_id] {
|
||||
for entry in cfg.basic_block_by_index(*basic_block_id) {
|
||||
match entry {
|
||||
// If the element is an assignment.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ fn main() {
|
|||
let ret = Parser::new(&allocator, &source_text, source_type).parse();
|
||||
|
||||
let program = allocator.alloc(ret.program);
|
||||
println!("{:#?}", &program);
|
||||
|
||||
let semantic = SemanticBuilder::new(&source_text, source_type)
|
||||
.with_check_syntax_error(true)
|
||||
.with_trivias(ret.trivias)
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ impl<'a> SemanticBuilder<'a> {
|
|||
if self.jsdoc.retrieve_jsdoc_comment(kind) {
|
||||
flags |= NodeFlags::JSDoc;
|
||||
}
|
||||
let ast_node = AstNode::new(kind, self.current_scope_id, flags);
|
||||
let ast_node = AstNode::new(kind, self.current_scope_id, self.cfg.current_node_ix, flags);
|
||||
let parent_node_id =
|
||||
if matches!(kind, AstKind::Program(_)) { None } else { Some(self.current_node_id) };
|
||||
self.current_node_id = self.nodes.add_node(ast_node, parent_node_id);
|
||||
|
|
@ -1012,6 +1012,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
/* cfg */
|
||||
|
||||
/* cfg - put unreachable after return */
|
||||
let _ = self.cfg.new_basic_block();
|
||||
self.cfg.put_unreachable();
|
||||
|
||||
self.cfg.after_statement(
|
||||
|
|
@ -1164,7 +1165,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
|
||||
let before_try_stmt_graph_ix = self.cfg.current_node_ix;
|
||||
let catch_block_graph_ix = self.cfg.new_basic_block();
|
||||
let catch_block_basic_block_id = self.cfg.current_basic_block;
|
||||
// every statement created with this active adds an edge from that node to this
|
||||
// catch block node
|
||||
//
|
||||
|
|
@ -1186,7 +1186,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
let optional_catch_block_graph_ix = if let Some(handler) = &stmt.handler {
|
||||
/* cfg */
|
||||
self.cfg.current_node_ix = catch_block_graph_ix;
|
||||
self.cfg.current_basic_block = catch_block_basic_block_id;
|
||||
/* cfg */
|
||||
|
||||
self.visit_catch_clause(handler);
|
||||
|
|
@ -1417,14 +1416,19 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
}
|
||||
flags
|
||||
});
|
||||
self.enter_node(kind);
|
||||
|
||||
/* cfg */
|
||||
let preserved = self.cfg.preserve_expression_state();
|
||||
|
||||
let before_function_graph_ix = self.cfg.current_node_ix;
|
||||
let function_graph_ix = self.cfg.new_basic_block_for_function();
|
||||
self.cfg.function_to_node_ix.insert(self.current_node_id, function_graph_ix);
|
||||
/* cfg */
|
||||
|
||||
// We add a new basic block to the cfg before entering the node
|
||||
// so that the correct cfg_ix is associated with the ast node.
|
||||
self.enter_node(kind);
|
||||
|
||||
/* cfg */
|
||||
self.cfg.add_edge(before_function_graph_ix, function_graph_ix, EdgeType::NewFunction);
|
||||
/* cfg */
|
||||
|
||||
|
|
@ -1510,17 +1514,20 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
fn visit_arrow_expression(&mut self, expr: &ArrowExpression<'a>) {
|
||||
let kind = AstKind::ArrowExpression(self.alloc(expr));
|
||||
self.enter_scope(ScopeFlags::Function | ScopeFlags::Arrow);
|
||||
|
||||
/* cfg */
|
||||
let preserved = self.cfg.preserve_expression_state();
|
||||
let current_node_ix = self.cfg.current_node_ix;
|
||||
let function_graph_ix = self.cfg.new_basic_block_for_function();
|
||||
/* cfg */
|
||||
|
||||
// We add a new basic block to the cfg before entering the node
|
||||
// so that the correct cfg_ix is associated with the ast node.
|
||||
self.enter_node(kind);
|
||||
|
||||
self.visit_formal_parameters(&expr.params);
|
||||
|
||||
/* cfg */
|
||||
let preserved = self.cfg.preserve_expression_state();
|
||||
|
||||
let current_basic_block_ix = self.cfg.current_basic_block;
|
||||
let current_node_ix = self.cfg.current_node_ix;
|
||||
let function_graph_ix = self.cfg.new_basic_block_for_function();
|
||||
self.cfg.function_to_node_ix.insert(self.current_node_id, function_graph_ix);
|
||||
self.cfg.add_edge(current_node_ix, function_graph_ix, EdgeType::NewFunction);
|
||||
if expr.expression {
|
||||
self.cfg.store_assignments_into_this_array.push(vec![]);
|
||||
|
|
@ -1531,7 +1538,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
|
||||
/* cfg */
|
||||
self.cfg.restore_expression_state(preserved);
|
||||
self.cfg.current_basic_block = current_basic_block_ix;
|
||||
self.cfg.current_node_ix = current_node_ix;
|
||||
// self.cfg.put_x_in_register(AssignmentValue::Function(self.current_node_id));
|
||||
/* cfg */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use oxc_span::Atom;
|
||||
use oxc_syntax::operator::{
|
||||
AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator,
|
||||
|
|
@ -117,13 +115,13 @@ pub enum EdgeType {
|
|||
NewFunction,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ControlFlowGraph {
|
||||
pub basic_blocks: Vec<Vec<BasicBlockElement>>,
|
||||
// note: this should only land in the big box for all things that take arguments
|
||||
// ie: callexpression, arrayexpression, etc
|
||||
// todo: add assert that it is used every time?
|
||||
pub use_this_register: Option<Register>,
|
||||
pub current_basic_block: usize,
|
||||
pub next_free_register: u32,
|
||||
pub store_assignments_into_this_array: Vec<Vec<Register>>,
|
||||
pub store_final_assignments_into_this_array: Vec<Vec<Register>>,
|
||||
|
|
@ -144,41 +142,31 @@ pub struct ControlFlowGraph {
|
|||
pub next_label: Option<Atom>,
|
||||
pub label_to_ast_node_ix: Vec<(Atom, AstNodeId)>,
|
||||
pub ast_node_to_break_continue: Vec<(AstNodeId, usize, Option<usize>)>,
|
||||
pub function_to_node_ix: HashMap<AstNodeId, NodeIndex>,
|
||||
pub after_throw_block: Option<NodeIndex>,
|
||||
}
|
||||
|
||||
impl Default for ControlFlowGraph {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlFlowGraph {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
basic_blocks: Vec::new(),
|
||||
use_this_register: None,
|
||||
current_basic_block: 0,
|
||||
next_free_register: 0,
|
||||
store_assignments_into_this_array: Vec::new(),
|
||||
store_final_assignments_into_this_array: Vec::new(),
|
||||
spread_indices: Vec::new(),
|
||||
should_save_stores_for_patching: false,
|
||||
saved_stores: Vec::new(),
|
||||
saved_store: None,
|
||||
graph: Graph::default(),
|
||||
// todo: invalid state
|
||||
current_node_ix: NodeIndex::default(),
|
||||
basic_blocks_with_breaks: Vec::new(),
|
||||
basic_blocks_with_continues: Vec::new(),
|
||||
switch_case_conditions: Vec::new(),
|
||||
next_label: None,
|
||||
label_to_ast_node_ix: Vec::new(),
|
||||
ast_node_to_break_continue: Vec::new(),
|
||||
function_to_node_ix: HashMap::new(),
|
||||
after_throw_block: None,
|
||||
}
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
pub fn basic_block_by_index(&self, index: NodeIndex) -> &Vec<BasicBlockElement> {
|
||||
let idx =
|
||||
*self.graph.node_weight(index).expect("expected a valid node index in self.graph");
|
||||
self.basic_blocks.get(idx).expect("expected a valid node index in self.basic_blocks")
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
pub fn basic_block_by_index_mut(&mut self, index: NodeIndex) -> &mut Vec<BasicBlockElement> {
|
||||
let idx =
|
||||
*self.graph.node_weight(index).expect("expected a valid node index in self.graph");
|
||||
self.basic_blocks.get_mut(idx).expect("expected a valid node index in self.basic_blocks")
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
pub fn current_basic_block(&mut self) -> &mut Vec<BasicBlockElement> {
|
||||
self.basic_block_by_index_mut(self.current_node_ix)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
@ -187,7 +175,6 @@ impl ControlFlowGraph {
|
|||
let basic_block_id = self.basic_blocks.len() - 1;
|
||||
let graph_index = self.graph.add_node(basic_block_id);
|
||||
self.current_node_ix = graph_index;
|
||||
self.current_basic_block = basic_block_id;
|
||||
|
||||
// todo: get smarter about what can throw, ie: return can't throw but it's expression can
|
||||
if let Some(after_throw_block) = self.after_throw_block {
|
||||
|
|
@ -200,10 +187,8 @@ impl ControlFlowGraph {
|
|||
#[must_use]
|
||||
pub fn new_basic_block(&mut self) -> NodeIndex {
|
||||
self.basic_blocks.push(Vec::new());
|
||||
let basic_block_id = self.basic_blocks.len() - 1;
|
||||
let graph_index = self.graph.add_node(basic_block_id);
|
||||
let graph_index = self.graph.add_node(self.basic_blocks.len() - 1);
|
||||
self.current_node_ix = graph_index;
|
||||
self.current_basic_block = basic_block_id;
|
||||
|
||||
// todo: get smarter about what can throw, ie: return can't throw but it's expression can
|
||||
if let Some(after_throw_block) = self.after_throw_block {
|
||||
|
|
@ -224,6 +209,7 @@ impl ControlFlowGraph {
|
|||
register
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
pub fn put_x_in_register(&mut self, asmt_value: AssignmentValue) {
|
||||
let register = self.use_this_register.take().unwrap_or_else(|| self.new_register());
|
||||
|
||||
|
|
@ -237,7 +223,7 @@ impl ControlFlowGraph {
|
|||
saved_store.0.push(basic_block_element);
|
||||
saved_store.1 = Some(register);
|
||||
} else {
|
||||
self.basic_blocks[self.current_basic_block].push(basic_block_element);
|
||||
self.current_basic_block().push(basic_block_element);
|
||||
}
|
||||
// used for storing the object base for MemberExpressions
|
||||
if let Some(arr) = self.store_assignments_into_this_array.last_mut() {
|
||||
|
|
@ -249,166 +235,21 @@ impl ControlFlowGraph {
|
|||
}
|
||||
}
|
||||
|
||||
// pub fn put_callee_with_arguments_in_register(
|
||||
// &mut self,
|
||||
// expression_ast_node_id: AstNodeId,
|
||||
// callee_node_index: Register,
|
||||
// call_type: CallType,
|
||||
// arguments: Vec<Register>,
|
||||
// ) {
|
||||
// let call_expression = AssignmentValue::CalleeWithArguments(
|
||||
// CalleeWithArgumentsAssignmentValue {
|
||||
// id: expression_ast_node_id,
|
||||
// callee: callee_node_index,
|
||||
// arguments,
|
||||
// spreads: self
|
||||
// .spread_indices
|
||||
// .pop()
|
||||
// .expect("expected there to be atleast one vec in the spread_indices"),
|
||||
// call_type,
|
||||
// }
|
||||
// .into(),
|
||||
// );
|
||||
|
||||
// self.put_x_in_register(call_expression);
|
||||
// }
|
||||
|
||||
// pub fn put_collection_in_register(
|
||||
// &mut self,
|
||||
// collection_ast_node_id: AstNodeId,
|
||||
// collection_type: CollectionType,
|
||||
// elements: Vec<Register>,
|
||||
// ) {
|
||||
// let array = AssignmentValue::Collection(
|
||||
// CollectionAssignmentValue {
|
||||
// id: collection_ast_node_id,
|
||||
// elements,
|
||||
// spreads: self
|
||||
// .spread_indices
|
||||
// .pop()
|
||||
// .expect("expected there to be a corresponding spread_indices for an array"),
|
||||
// collection_type,
|
||||
// }
|
||||
// .into(),
|
||||
// );
|
||||
|
||||
// self.put_x_in_register(array);
|
||||
// }
|
||||
|
||||
// pub fn put_constant_in_register(&mut self, constant_ast_node_id: AstNodeId) {
|
||||
// self.put_x_in_register(AssignmentValue::Constant(constant_ast_node_id));
|
||||
// }
|
||||
|
||||
// pub fn put_reference_in_register(&mut self, constant_ast_node_id: AstNodeId) {
|
||||
// self.put_x_in_register(AssignmentValue::Reference(constant_ast_node_id));
|
||||
// }
|
||||
|
||||
pub fn put_throw(&mut self, throw_expr: Register) {
|
||||
self.basic_blocks[self.current_basic_block].push(BasicBlockElement::Throw(throw_expr));
|
||||
self.current_basic_block().push(BasicBlockElement::Throw(throw_expr));
|
||||
}
|
||||
|
||||
pub fn put_unreachable(&mut self) {
|
||||
let current_node_ix = self.current_node_ix;
|
||||
let basic_block_with_unreachable_graph_ix = self.new_basic_block();
|
||||
self.add_edge(current_node_ix, basic_block_with_unreachable_graph_ix, EdgeType::Normal);
|
||||
self.basic_blocks[self.current_basic_block].push(BasicBlockElement::Unreachable);
|
||||
self.current_basic_block().push(BasicBlockElement::Unreachable);
|
||||
}
|
||||
|
||||
pub fn put_undefined(&mut self) {
|
||||
self.put_x_in_register(AssignmentValue::ImplicitUndefined);
|
||||
}
|
||||
|
||||
// pub fn visit_computed_member_expression(
|
||||
// &mut self,
|
||||
// expr: &ComputedMemberExpression,
|
||||
// ast_node_id: AstNodeId,
|
||||
// ) {
|
||||
// let basic_block = &mut self.basic_blocks[self.current_basic_block];
|
||||
// let removed = self.saved_stores.pop();
|
||||
// if let Some((saved_nodes, Some(last_register))) = removed {
|
||||
// if self.should_save_stores_for_patching {
|
||||
// let saved_store = self.saved_stores.last_mut().expect(
|
||||
// "expected a saved_store if self.should_save_stores_for_patching is true",
|
||||
// );
|
||||
// saved_store.0.extend(saved_nodes);
|
||||
// saved_store.1 = Some(last_register);
|
||||
// } else {
|
||||
// basic_block.extend(saved_nodes);
|
||||
// }
|
||||
|
||||
// let accessed_on = {
|
||||
// let asmts = self
|
||||
// .store_assignments_into_this_array
|
||||
// .pop()
|
||||
// .expect("expected store_assignments_into_this_array to have an element");
|
||||
// debug_assert!(!asmts.is_empty(), "there should be at least the object base here");
|
||||
// asmts[asmts.len() - 1]
|
||||
// };
|
||||
|
||||
// self.put_x_in_register(AssignmentValue::ObjectPropertyAccess(
|
||||
// ObjectPropertyAccessAssignmentValue {
|
||||
// id: ast_node_id,
|
||||
// access_by: ObjectPropertyAccessBy::Expression(last_register),
|
||||
// optional: expr.optional,
|
||||
// access_on: accessed_on,
|
||||
// }
|
||||
// .into(),
|
||||
// ));
|
||||
// } else {
|
||||
// panic!("No saved_node_indices in self.saved_stores")
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn visit_static_member_expression(
|
||||
// &mut self,
|
||||
// expr: &StaticMemberExpression,
|
||||
// ast_node_id: AstNodeId,
|
||||
// ) {
|
||||
// let accessed_on = {
|
||||
// let asmts = self
|
||||
// .store_assignments_into_this_array
|
||||
// .pop()
|
||||
// .expect("expected store_assignments_into_this_array to have an element");
|
||||
// debug_assert!(!asmts.is_empty(), "there should be at least the object base here");
|
||||
// asmts[asmts.len() - 1]
|
||||
// };
|
||||
|
||||
// self.put_x_in_register(AssignmentValue::ObjectPropertyAccess(
|
||||
// ObjectPropertyAccessAssignmentValue {
|
||||
// id: ast_node_id,
|
||||
// access_by: ObjectPropertyAccessBy::Property(expr.property.name.clone()), // todo: should we be cloning here?
|
||||
// optional: expr.optional,
|
||||
// access_on: accessed_on,
|
||||
// }
|
||||
// .into(),
|
||||
// ));
|
||||
// }
|
||||
|
||||
// pub fn visit_private_field_expression(
|
||||
// &mut self,
|
||||
// expr: &PrivateFieldExpression,
|
||||
// ast_node_id: AstNodeId,
|
||||
// ) {
|
||||
// let accessed_on = {
|
||||
// let asmts = self
|
||||
// .store_assignments_into_this_array
|
||||
// .pop()
|
||||
// .expect("expected store_assignments_into_this_array to have an element");
|
||||
// debug_assert!(!asmts.is_empty(), "there should be at least the object base here");
|
||||
// asmts[asmts.len() - 1]
|
||||
// };
|
||||
|
||||
// self.put_x_in_register(AssignmentValue::ObjectPropertyAccess(
|
||||
// ObjectPropertyAccessAssignmentValue {
|
||||
// id: ast_node_id,
|
||||
// access_by: ObjectPropertyAccessBy::PrivateProperty(expr.field.name.clone()), // todo: should we be cloning here?
|
||||
// optional: expr.optional,
|
||||
// access_on: accessed_on,
|
||||
// }
|
||||
// .into(),
|
||||
// ));
|
||||
// }
|
||||
|
||||
#[must_use]
|
||||
pub fn preserve_expression_state(&mut self) -> PreservedExpressionState {
|
||||
let use_this_register = self.use_this_register.take();
|
||||
|
|
@ -471,6 +312,7 @@ impl ControlFlowGraph {
|
|||
pss
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
pub fn after_statement(
|
||||
&mut self,
|
||||
preserved_state: &PreservedStatementState,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ pub use petgraph;
|
|||
|
||||
pub use builder::{SemanticBuilder, SemanticBuilderReturn};
|
||||
use class::ClassTable;
|
||||
use control_flow::ControlFlowGraph;
|
||||
pub use jsdoc::{JSDoc, JSDocComment, JSDocTag};
|
||||
use oxc_ast::{ast::IdentifierReference, AstKind, TriviasMap};
|
||||
use oxc_span::SourceType;
|
||||
|
|
@ -34,8 +33,8 @@ pub use crate::{
|
|||
builder::VariableInfo,
|
||||
control_flow::{
|
||||
print_basic_block, AssignmentValue, BasicBlockElement, BinaryAssignmentValue, BinaryOp,
|
||||
CallType, CalleeWithArgumentsAssignmentValue, CollectionAssignmentValue, EdgeType,
|
||||
ObjectPropertyAccessAssignmentValue, Register, UnaryExpressioneAssignmentValue,
|
||||
CallType, CalleeWithArgumentsAssignmentValue, CollectionAssignmentValue, ControlFlowGraph,
|
||||
EdgeType, ObjectPropertyAccessAssignmentValue, Register, UnaryExpressioneAssignmentValue,
|
||||
UpdateAssignmentValue,
|
||||
},
|
||||
node::{AstNode, AstNodeId, AstNodes},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use petgraph::stable_graph::NodeIndex;
|
||||
|
||||
use oxc_ast::AstKind;
|
||||
use oxc_index::IndexVec;
|
||||
|
||||
|
|
@ -15,18 +17,25 @@ pub struct AstNode<'a> {
|
|||
/// Associated Scope (initialized by binding)
|
||||
scope_id: ScopeId,
|
||||
|
||||
/// Associated NodeIndex in CFG (initialized by control_flow)
|
||||
cfg_ix: NodeIndex,
|
||||
|
||||
flags: NodeFlags,
|
||||
}
|
||||
|
||||
impl<'a> AstNode<'a> {
|
||||
pub fn new(kind: AstKind<'a>, scope_id: ScopeId, flags: NodeFlags) -> Self {
|
||||
Self { id: AstNodeId::new(0), kind, scope_id, flags }
|
||||
pub fn new(kind: AstKind<'a>, scope_id: ScopeId, cfg_ix: NodeIndex, flags: NodeFlags) -> Self {
|
||||
Self { id: AstNodeId::new(0), kind, cfg_ix, scope_id, flags }
|
||||
}
|
||||
|
||||
pub fn id(&self) -> AstNodeId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn cfg_ix(&self) -> NodeIndex {
|
||||
self.cfg_ix
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> AstKind<'a> {
|
||||
self.kind
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,14 +19,13 @@ pub fn neighbors_filtered_by_edge_weight<
|
|||
) -> Vec<State>
|
||||
where
|
||||
F: Fn(&EdgeWeight) -> Option<State>,
|
||||
G: FnMut(&NodeWeight, State) -> (State, bool),
|
||||
G: FnMut(&NodeIndex, State) -> (State, bool),
|
||||
{
|
||||
let mut q = vec![];
|
||||
let mut final_states = vec![];
|
||||
|
||||
// for initial node
|
||||
let (new_state, keep_walking_this_path) =
|
||||
visitor(graph.node_weight(node).unwrap(), Default::default());
|
||||
let (new_state, keep_walking_this_path) = visitor(&node, Default::default());
|
||||
// if we will continue walking push this node
|
||||
if keep_walking_this_path {
|
||||
q.push((node, new_state));
|
||||
|
|
@ -42,7 +41,7 @@ where
|
|||
} else {
|
||||
let opposite_dir_of_edge_graph_ix = edge.target();
|
||||
let (new_state, keep_walking_this_path) =
|
||||
visitor(graph.node_weight(opposite_dir_of_edge_graph_ix).unwrap(), state);
|
||||
visitor(&opposite_dir_of_edge_graph_ix, state);
|
||||
if keep_walking_this_path {
|
||||
q.push((opposite_dir_of_edge_graph_ix, new_state));
|
||||
} else {
|
||||
|
|
@ -65,13 +64,13 @@ pub fn replicate_tree_to_leaves(
|
|||
start_at: NodeIndex,
|
||||
cfg: &mut ControlFlowGraph,
|
||||
) -> HashMap<NodeIndex, NodeIndex> {
|
||||
fn duplicate_graph_node(basic_block_id: usize, cfg: &mut ControlFlowGraph) -> NodeIndex {
|
||||
fn duplicate_graph_node(basic_block_id: NodeIndex, cfg: &mut ControlFlowGraph) -> NodeIndex {
|
||||
let new_basic_block_graph_ix = cfg.new_basic_block();
|
||||
|
||||
// todo: what's a better way to do this?
|
||||
for i in 0..cfg.basic_blocks[basic_block_id].len() {
|
||||
let item = cfg.basic_blocks[basic_block_id][i].clone();
|
||||
cfg.basic_blocks[cfg.current_basic_block].push(item);
|
||||
let items = cfg.basic_block_by_index(basic_block_id).clone();
|
||||
for item in items {
|
||||
cfg.current_basic_block().push(item);
|
||||
}
|
||||
|
||||
// todo: should we add the functions copied here to the function_to_node_ix?
|
||||
|
|
@ -80,12 +79,11 @@ pub fn replicate_tree_to_leaves(
|
|||
}
|
||||
|
||||
let preserved_graph_ix = cfg.current_node_ix;
|
||||
let preserved_bb = cfg.current_basic_block;
|
||||
|
||||
let mut old_to_new: HashMap<NodeIndex, NodeIndex> = HashMap::default();
|
||||
let mut q = vec![];
|
||||
|
||||
let new_graph_start_ix = duplicate_graph_node(*cfg.graph.node_weight(start_at).unwrap(), cfg);
|
||||
let new_graph_start_ix = duplicate_graph_node(start_at, cfg);
|
||||
old_to_new.insert(start_at, new_graph_start_ix);
|
||||
q.push((new_graph_start_ix, start_at));
|
||||
let mut edges_finished_already = HashSet::new();
|
||||
|
|
@ -108,7 +106,7 @@ pub fn replicate_tree_to_leaves(
|
|||
let new_graph_ix = if old_to_new.contains_key(&to) {
|
||||
old_to_new[&to]
|
||||
} else {
|
||||
let new_graph_ix = duplicate_graph_node(*cfg.graph.node_weight(to).unwrap(), cfg);
|
||||
let new_graph_ix = duplicate_graph_node(to, cfg);
|
||||
old_to_new.insert(node.1, new_graph_start_ix);
|
||||
new_graph_ix
|
||||
};
|
||||
|
|
@ -119,7 +117,6 @@ pub fn replicate_tree_to_leaves(
|
|||
}
|
||||
}
|
||||
|
||||
cfg.current_basic_block = preserved_bb;
|
||||
cfg.current_node_ix = preserved_graph_ix;
|
||||
old_to_new
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/class_extend_super.js
|
|||
digraph {
|
||||
0 [ label = ""]
|
||||
1 [ label = "$return = <value>"]
|
||||
2 [ label = "Unreachable()"]
|
||||
3 [ label = ""]
|
||||
2 [ label = ""]
|
||||
3 [ label = "Unreachable()"]
|
||||
4 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
1 -> 2 [ ]
|
||||
0 -> 3 [ ]
|
||||
2 -> 3 [ ]
|
||||
0 -> 4 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ bb1: {
|
|||
}
|
||||
|
||||
bb2: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb3: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb4: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/fn_return_obj_expr_with_compu
|
|||
digraph {
|
||||
0 [ label = ""]
|
||||
1 [ label = "$return = <value>"]
|
||||
2 [ label = "Unreachable()"]
|
||||
3 [ label = ""]
|
||||
2 [ label = ""]
|
||||
3 [ label = "Unreachable()"]
|
||||
4 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
1 -> 2 [ ]
|
||||
0 -> 3 [ ]
|
||||
2 -> 3 [ ]
|
||||
0 -> 4 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ bb1: {
|
|||
}
|
||||
|
||||
bb2: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb3: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb4: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,22 +7,25 @@ digraph {
|
|||
0 [ label = ""]
|
||||
1 [ label = ""]
|
||||
2 [ label = "$return = <value>"]
|
||||
3 [ label = "Unreachable()"]
|
||||
4 [ label = "$return = <value>"]
|
||||
5 [ label = "Unreachable()"]
|
||||
3 [ label = ""]
|
||||
4 [ label = "Unreachable()"]
|
||||
5 [ label = "$return = <value>"]
|
||||
6 [ label = ""]
|
||||
7 [ label = "Unreachable()\n$return = <value>"]
|
||||
8 [ label = "Unreachable()"]
|
||||
9 [ label = ""]
|
||||
7 [ label = "Unreachable()"]
|
||||
8 [ label = ""]
|
||||
9 [ label = "Unreachable()\n$return = <value>"]
|
||||
10 [ label = ""]
|
||||
11 [ label = "Unreachable()"]
|
||||
12 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
2 -> 3 [ ]
|
||||
4 -> 5 [ ]
|
||||
3 -> 4 [ ]
|
||||
6 -> 7 [ ]
|
||||
3 -> 6 [ ]
|
||||
8 -> 9 [ ]
|
||||
4 -> 8 [ ]
|
||||
1 -> 2 [ ]
|
||||
1 -> 4 [ ]
|
||||
5 -> 6 [ ]
|
||||
1 -> 5 [ ]
|
||||
7 -> 8 [ ]
|
||||
0 -> 9 [ ]
|
||||
10 -> 11 [ ]
|
||||
0 -> 12 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@ bb2: {
|
|||
}
|
||||
|
||||
bb3: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb4: {
|
||||
$return = <value>
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb5: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb6: {
|
||||
|
|
@ -33,13 +33,25 @@ bb6: {
|
|||
|
||||
bb7: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb8: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb9: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb10: {
|
||||
|
||||
}
|
||||
|
||||
bb11: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb12: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,30 +8,33 @@ digraph {
|
|||
1 [ label = ""]
|
||||
2 [ label = ""]
|
||||
3 [ label = "$return = <value>"]
|
||||
4 [ label = "Unreachable()"]
|
||||
4 [ label = ""]
|
||||
5 [ label = "Unreachable()"]
|
||||
6 [ label = ""]
|
||||
7 [ label = "Unreachable()"]
|
||||
8 [ label = "$return = <value>"]
|
||||
9 [ label = ""]
|
||||
10 [ label = "Unreachable()"]
|
||||
6 [ label = "Unreachable()"]
|
||||
7 [ label = ""]
|
||||
8 [ label = "Unreachable()"]
|
||||
9 [ label = "$return = <value>"]
|
||||
10 [ label = ""]
|
||||
11 [ label = "Unreachable()"]
|
||||
12 [ label = ""]
|
||||
13 [ label = "Unreachable()"]
|
||||
14 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
3 -> 2 [ ]
|
||||
4 -> 2 [ ]
|
||||
3 -> 4 [ ]
|
||||
2 -> 5 [ ]
|
||||
6 -> 7 [ ]
|
||||
1 -> 3 [ ]
|
||||
9 -> 10 [ ]
|
||||
4 -> 6 [ ]
|
||||
5 -> 2 [ ]
|
||||
4 -> 5 [ ]
|
||||
2 -> 6 [ ]
|
||||
7 -> 8 [ ]
|
||||
4 -> 2 [ ]
|
||||
2 -> 9 [ ]
|
||||
1 -> 9 [ ]
|
||||
6 -> 8 [ ]
|
||||
8 -> 11 [ ]
|
||||
0 -> 12 [ ]
|
||||
1 -> 3 [ ]
|
||||
10 -> 11 [ ]
|
||||
5 -> 7 [ ]
|
||||
8 -> 9 [ ]
|
||||
5 -> 2 [ ]
|
||||
2 -> 10 [ ]
|
||||
1 -> 10 [ ]
|
||||
7 -> 9 [ ]
|
||||
12 -> 13 [ ]
|
||||
0 -> 14 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ bb3: {
|
|||
}
|
||||
|
||||
bb4: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb5: {
|
||||
|
|
@ -28,23 +28,23 @@ bb5: {
|
|||
}
|
||||
|
||||
bb6: {
|
||||
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb7: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb8: {
|
||||
$return = <value>
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb9: {
|
||||
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb10: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb11: {
|
||||
|
|
@ -54,3 +54,11 @@ bb11: {
|
|||
bb12: {
|
||||
|
||||
}
|
||||
|
||||
bb13: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb14: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/member_access_with_numbered_i
|
|||
digraph {
|
||||
0 [ label = ""]
|
||||
1 [ label = "$return = <value>"]
|
||||
2 [ label = "Unreachable()\n$return = <value>"]
|
||||
3 [ label = "Unreachable()"]
|
||||
2 [ label = ""]
|
||||
3 [ label = "Unreachable()\n$return = <value>"]
|
||||
4 [ label = ""]
|
||||
5 [ label = "Unreachable()"]
|
||||
6 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
1 -> 2 [ ]
|
||||
2 -> 3 [ ]
|
||||
0 -> 4 [ ]
|
||||
4 -> 5 [ ]
|
||||
0 -> 6 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,14 +12,22 @@ bb1: {
|
|||
}
|
||||
|
||||
bb2: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
|
||||
}
|
||||
|
||||
bb3: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb4: {
|
||||
|
||||
}
|
||||
|
||||
bb5: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb6: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/member_access_with_unreachabl
|
|||
digraph {
|
||||
0 [ label = ""]
|
||||
1 [ label = "$return = <value>"]
|
||||
2 [ label = "Unreachable()\n$return = <value>"]
|
||||
3 [ label = "Unreachable()"]
|
||||
2 [ label = ""]
|
||||
3 [ label = "Unreachable()\n$return = <value>"]
|
||||
4 [ label = ""]
|
||||
5 [ label = "Unreachable()"]
|
||||
6 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
1 -> 2 [ ]
|
||||
2 -> 3 [ ]
|
||||
0 -> 4 [ ]
|
||||
4 -> 5 [ ]
|
||||
0 -> 6 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,14 +12,22 @@ bb1: {
|
|||
}
|
||||
|
||||
bb2: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
|
||||
}
|
||||
|
||||
bb3: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb4: {
|
||||
|
||||
}
|
||||
|
||||
bb5: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb6: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/nested_computed_member_expres
|
|||
digraph {
|
||||
0 [ label = ""]
|
||||
1 [ label = "$return = <value>"]
|
||||
2 [ label = "Unreachable()"]
|
||||
3 [ label = ""]
|
||||
2 [ label = ""]
|
||||
3 [ label = "Unreachable()"]
|
||||
4 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
1 -> 2 [ ]
|
||||
0 -> 3 [ ]
|
||||
2 -> 3 [ ]
|
||||
0 -> 4 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ bb1: {
|
|||
}
|
||||
|
||||
bb2: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb3: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb4: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/private_in.js
|
|||
digraph {
|
||||
0 [ label = ""]
|
||||
1 [ label = "$return = <value>"]
|
||||
2 [ label = "Unreachable()"]
|
||||
3 [ label = ""]
|
||||
2 [ label = ""]
|
||||
3 [ label = "Unreachable()"]
|
||||
4 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
1 -> 2 [ ]
|
||||
0 -> 3 [ ]
|
||||
2 -> 3 [ ]
|
||||
0 -> 4 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ bb1: {
|
|||
}
|
||||
|
||||
bb2: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb3: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb4: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/simple_spread.js
|
|||
digraph {
|
||||
0 [ label = ""]
|
||||
1 [ label = "$return = <value>"]
|
||||
2 [ label = "Unreachable()"]
|
||||
3 [ label = ""]
|
||||
2 [ label = ""]
|
||||
3 [ label = "Unreachable()"]
|
||||
4 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
1 -> 2 [ ]
|
||||
0 -> 3 [ ]
|
||||
2 -> 3 [ ]
|
||||
0 -> 4 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ bb1: {
|
|||
}
|
||||
|
||||
bb2: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb3: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb4: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/spread_in_a_spread.js
|
|||
digraph {
|
||||
0 [ label = ""]
|
||||
1 [ label = "$return = <value>"]
|
||||
2 [ label = "Unreachable()"]
|
||||
3 [ label = ""]
|
||||
2 [ label = ""]
|
||||
3 [ label = "Unreachable()"]
|
||||
4 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
1 -> 2 [ ]
|
||||
0 -> 3 [ ]
|
||||
2 -> 3 [ ]
|
||||
0 -> 4 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ bb1: {
|
|||
}
|
||||
|
||||
bb2: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb3: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb4: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,14 +16,17 @@ digraph {
|
|||
9 [ label = "Unreachable()"]
|
||||
10 [ label = ""]
|
||||
11 [ label = "$return = <value>"]
|
||||
12 [ label = "Unreachable()"]
|
||||
13 [ label = ""]
|
||||
12 [ label = ""]
|
||||
13 [ label = "Unreachable()"]
|
||||
14 [ label = ""]
|
||||
15 [ label = ""]
|
||||
16 [ label = "$return = <value>"]
|
||||
17 [ label = "Unreachable()\n$return = <value>"]
|
||||
18 [ label = "Unreachable()"]
|
||||
19 [ label = ""]
|
||||
16 [ label = ""]
|
||||
17 [ label = "$return = <value>"]
|
||||
18 [ label = ""]
|
||||
19 [ label = "Unreachable()\n$return = <value>"]
|
||||
20 [ label = ""]
|
||||
21 [ label = "Unreachable()"]
|
||||
22 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
2 -> 3 [ ]
|
||||
3 -> 4 [ ]
|
||||
|
|
@ -33,38 +36,38 @@ digraph {
|
|||
8 -> 9 [ ]
|
||||
8 -> 9 [ ]
|
||||
10 -> 11 [ ]
|
||||
11 -> 12 [ ]
|
||||
13 -> 14 [ ]
|
||||
15 -> 16 [ ]
|
||||
12 -> 13 [ ]
|
||||
14 -> 15 [ ]
|
||||
16 -> 17 [ ]
|
||||
18 -> 19 [ ]
|
||||
2 -> 5 [ ]
|
||||
2 -> 7 [ ]
|
||||
2 -> 10 [ ]
|
||||
2 -> 13 [ ]
|
||||
2 -> 15 [ ]
|
||||
2 -> 14 [ ]
|
||||
2 -> 16 [ ]
|
||||
4 -> 5 [ ]
|
||||
1 -> 2 [ ]
|
||||
5 -> 7 [ ]
|
||||
5 -> 10 [ ]
|
||||
5 -> 13 [ ]
|
||||
5 -> 15 [ ]
|
||||
5 -> 14 [ ]
|
||||
5 -> 16 [ ]
|
||||
6 -> 7 [ ]
|
||||
1 -> 5 [ ]
|
||||
7 -> 10 [ ]
|
||||
7 -> 13 [ ]
|
||||
7 -> 15 [ ]
|
||||
7 -> 14 [ ]
|
||||
7 -> 16 [ ]
|
||||
9 -> 10 [ ]
|
||||
1 -> 7 [ ]
|
||||
10 -> 13 [ ]
|
||||
10 -> 15 [ ]
|
||||
12 -> 13 [ ]
|
||||
10 -> 14 [ ]
|
||||
10 -> 16 [ ]
|
||||
13 -> 14 [ ]
|
||||
1 -> 10 [ ]
|
||||
13 -> 15 [ ]
|
||||
14 -> 15 [ ]
|
||||
1 -> 13 [ ]
|
||||
1 -> 15 [ ]
|
||||
15 -> 17 [ ]
|
||||
17 -> 18 [ ]
|
||||
0 -> 19 [ ]
|
||||
14 -> 16 [ ]
|
||||
15 -> 16 [ ]
|
||||
1 -> 14 [ ]
|
||||
1 -> 16 [ ]
|
||||
16 -> 19 [ ]
|
||||
20 -> 21 [ ]
|
||||
0 -> 22 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,11 +52,11 @@ bb11: {
|
|||
}
|
||||
|
||||
bb12: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb13: {
|
||||
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb14: {
|
||||
|
|
@ -68,18 +68,30 @@ bb15: {
|
|||
}
|
||||
|
||||
bb16: {
|
||||
$return = <value>
|
||||
|
||||
}
|
||||
|
||||
bb17: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb18: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb19: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb20: {
|
||||
|
||||
}
|
||||
|
||||
bb21: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb22: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,16 +6,18 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/two_functions.js
|
|||
digraph {
|
||||
0 [ label = ""]
|
||||
1 [ label = "$return = <value>"]
|
||||
2 [ label = "Unreachable()"]
|
||||
3 [ label = ""]
|
||||
4 [ label = "$return = <value>"]
|
||||
5 [ label = "Unreachable()"]
|
||||
2 [ label = ""]
|
||||
3 [ label = "Unreachable()"]
|
||||
4 [ label = ""]
|
||||
5 [ label = "$return = <value>"]
|
||||
6 [ label = ""]
|
||||
7 [ label = "Unreachable()"]
|
||||
8 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
1 -> 2 [ ]
|
||||
0 -> 3 [ ]
|
||||
3 -> 4 [ ]
|
||||
2 -> 3 [ ]
|
||||
0 -> 4 [ ]
|
||||
4 -> 5 [ ]
|
||||
3 -> 6 [ ]
|
||||
6 -> 7 [ ]
|
||||
4 -> 8 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,21 +12,29 @@ bb1: {
|
|||
}
|
||||
|
||||
bb2: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb3: {
|
||||
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb4: {
|
||||
$return = <value>
|
||||
|
||||
}
|
||||
|
||||
bb5: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb6: {
|
||||
|
||||
}
|
||||
|
||||
bb7: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb8: {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,18 +8,20 @@ digraph {
|
|||
1 [ label = ""]
|
||||
2 [ label = ""]
|
||||
3 [ label = "$return = <value>"]
|
||||
4 [ label = "Unreachable()"]
|
||||
5 [ label = "$return = <value>"]
|
||||
6 [ label = "Unreachable()"]
|
||||
4 [ label = ""]
|
||||
5 [ label = "Unreachable()"]
|
||||
6 [ label = "$return = <value>"]
|
||||
7 [ label = ""]
|
||||
8 [ label = "Unreachable()"]
|
||||
9 [ label = ""]
|
||||
0 -> 1 [ ]
|
||||
3 -> 4 [ ]
|
||||
4 -> 5 [ ]
|
||||
1 -> 2 [ ]
|
||||
2 -> 3 [ ]
|
||||
3 -> 5 [ ]
|
||||
3 -> 6 [ ]
|
||||
3 -> 2 [ ]
|
||||
2 -> 5 [ ]
|
||||
5 -> 6 [ ]
|
||||
0 -> 7 [ ]
|
||||
2 -> 6 [ ]
|
||||
7 -> 8 [ ]
|
||||
0 -> 9 [ ]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,17 +20,25 @@ bb3: {
|
|||
}
|
||||
|
||||
bb4: {
|
||||
Unreachable()
|
||||
|
||||
}
|
||||
|
||||
bb5: {
|
||||
$return = <value>
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb6: {
|
||||
Unreachable()
|
||||
$return = <value>
|
||||
}
|
||||
|
||||
bb7: {
|
||||
|
||||
}
|
||||
|
||||
bb8: {
|
||||
Unreachable()
|
||||
}
|
||||
|
||||
bb9: {
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue