- BoundBlock.Ordinal
- Scope end
This commit is contained in:
Jakub Míšek 2016-03-04 10:19:54 +01:00
Родитель 54b8d976e2
Коммит 1555bc73e7
3 изменённых файлов: 144 добавлений и 45 удалений

Просмотреть файл

@ -42,6 +42,28 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
internal set { _next = value; } internal set { _next = value; }
} }
#region Topological order & scoping
/// <summary>
/// Gets block topological index.
/// Index is unique within the graph.
/// </summary>
public int Ordinal { get { return _ordinal; } internal set { _ordinal = value; } }
private int _ordinal;
/// <summary>
/// Index of nearest block after the scope.
/// </summary>
public int ScopeTo { get { return _scopeTo; } internal set { _scopeTo = value; } }
private int _scopeTo;
/// <summary>
/// Gets value indicating <see cref="ScopeTo"/> is valid.
/// </summary>
public bool ScopeToValid => _scopeTo >= _ordinal;
#endregion
internal BoundBlock() internal BoundBlock()
{ {
_statements = new List<BoundStatement>(); _statements = new List<BoundStatement>();
@ -150,7 +172,6 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
private readonly VariableName _variableName; private readonly VariableName _variableName;
public CatchBlock(CatchItem item) public CatchBlock(CatchItem item)
: base()
{ {
_typeRef = item.TypeRef; _typeRef = item.TypeRef;
_variableName = item.Variable.VarName; _variableName = item.Variable.VarName;
@ -177,7 +198,6 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
public bool IsDefault => _caseValue == null; public bool IsDefault => _caseValue == null;
public CaseBlock(BoundExpression caseValue) public CaseBlock(BoundExpression caseValue)
: base()
{ {
_caseValue = caseValue; _caseValue = caseValue;
} }

Просмотреть файл

@ -20,6 +20,8 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
private Dictionary<string, ControlFlowGraph.LabelBlockState> _labels; private Dictionary<string, ControlFlowGraph.LabelBlockState> _labels;
private List<BreakTargetScope> _breakTargets; private List<BreakTargetScope> _breakTargets;
private Stack<TryCatchEdge> _tryTargets; private Stack<TryCatchEdge> _tryTargets;
private Stack<LocalScopeInfo> _scopes = new Stack<LocalScopeInfo>(1);
private int _index = 0;
public BoundBlock/*!*/Start { get; private set; } public BoundBlock/*!*/Start { get; private set; }
public BoundBlock/*!*/Exit { get; private set; } public BoundBlock/*!*/Exit { get; private set; }
@ -39,6 +41,34 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
public List<BoundBlock>/*!*/DeadBlocks { get { return _deadBlocks; } } public List<BoundBlock>/*!*/DeadBlocks { get { return _deadBlocks; } }
private readonly List<BoundBlock>/*!*/_deadBlocks = new List<BoundBlock>(); private readonly List<BoundBlock>/*!*/_deadBlocks = new List<BoundBlock>();
#region LocalScope
private class LocalScopeInfo
{
public BoundBlock FirstBlock => _firstblock;
private BoundBlock _firstblock;
public LocalScopeInfo(BoundBlock firstBlock)
{
_firstblock = firstBlock;
}
}
private void OpenScope(BoundBlock block)
{
_scopes.Push(new LocalScopeInfo(block));
}
private void CloseScope()
{
if (_scopes.Count == 0)
throw new InvalidOperationException();
_scopes.Pop().FirstBlock.ScopeTo = _index;
}
#endregion
#region BreakTargetScope #region BreakTargetScope
/// <summary> /// <summary>
@ -56,22 +86,22 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
} }
} }
private BreakTargetScope GetBreakTarget(int level) private BreakTargetScope GetBreakScope(int level)
{ {
if (level < 1) level = 1; if (level < 1) level = 1; // PHP behavior
if (_breakTargets == null || _breakTargets.Count < level) if (_breakTargets == null || _breakTargets.Count < level)
return default(BreakTargetScope); return default(BreakTargetScope);
return _breakTargets[_breakTargets.Count - level]; return _breakTargets[_breakTargets.Count - level];
} }
private void EnterBreakTarget(BoundBlock breakBlock, BoundBlock continueBlock) private void OpenBreakScope(BoundBlock breakBlock, BoundBlock continueBlock)
{ {
if (_breakTargets == null) _breakTargets = new List<BreakTargetScope>(1); if (_breakTargets == null) _breakTargets = new List<BreakTargetScope>(1);
_breakTargets.Add(new BreakTargetScope(breakBlock, continueBlock)); _breakTargets.Add(new BreakTargetScope(breakBlock, continueBlock));
} }
private void ExitBreakTarget() private void CloseBreakScope()
{ {
Debug.Assert(_breakTargets != null && _breakTargets.Count != 0); Debug.Assert(_breakTargets != null && _breakTargets.Count != 0);
_breakTargets.RemoveAt(_breakTargets.Count - 1); _breakTargets.RemoveAt(_breakTargets.Count - 1);
@ -81,21 +111,23 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
#region TryTargetScope #region TryTargetScope
private TryCatchEdge GetTryTarget() private TryCatchEdge CurrentTryScope
{ {
if (_tryTargets == null || _tryTargets.Count == 0) get
return null; {
return (_tryTargets != null && _tryTargets.Count != 0)
return _tryTargets.Peek(); ? _tryTargets.Peek()
: null;
}
} }
private void EnterTryTarget(TryCatchEdge edge) private void OpenTryScope(TryCatchEdge edge)
{ {
if (_tryTargets == null) _tryTargets = new Stack<TryCatchEdge>(); if (_tryTargets == null) _tryTargets = new Stack<TryCatchEdge>();
_tryTargets.Push(edge); _tryTargets.Push(edge);
} }
private void ExitTryTarget() private void CloeTryScope()
{ {
Debug.Assert(_tryTargets != null && _tryTargets.Count != 0); Debug.Assert(_tryTargets != null && _tryTargets.Count != 0);
_tryTargets.Pop(); _tryTargets.Pop();
@ -115,10 +147,14 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
this.Start = new StartBlock(); this.Start = new StartBlock();
this.Exit = new ExitBlock(); this.Exit = new ExitBlock();
_current = this.Start; _current = WithOpenScope(this.Start);
statements.ForEach(this.VisitElement); statements.ForEach(this.VisitElement);
_current = Connect(_current, this.Exit); _current = Connect(_current, this.Exit);
//
WithNewOrdinal(this.Exit);
CloseScope();
} }
public static BuilderVisitor/*!*/Build(IList<Statement>/*!*/statements, SemanticsBinder/*!*/binder) public static BuilderVisitor/*!*/Build(IList<Statement>/*!*/statements, SemanticsBinder/*!*/binder)
@ -144,7 +180,7 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
private BoundBlock/*!*/NewBlock() private BoundBlock/*!*/NewBlock()
{ {
return new BoundBlock(); return WithNewOrdinal(new BoundBlock());
} }
/// <summary> /// <summary>
@ -153,14 +189,14 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
/// </summary> /// </summary>
private BoundBlock/*!*/NewDeadBlock() private BoundBlock/*!*/NewDeadBlock()
{ {
var block = NewBlock(); var block = new BoundBlock();
_deadBlocks.Add(block); _deadBlocks.Add(block);
return block; return block;
} }
private CatchBlock/*!*/NewBlock(CatchItem item) private CatchBlock/*!*/NewBlock(CatchItem item)
{ {
return new CatchBlock(item); return WithNewOrdinal(new CatchBlock(item));
} }
private CaseBlock/*!*/NewBlock(SwitchItem item) private CaseBlock/*!*/NewBlock(SwitchItem item)
@ -168,7 +204,7 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
var caseitem = item as CaseItem; var caseitem = item as CaseItem;
BoundExpression caseValue = // null => DefaultItem BoundExpression caseValue = // null => DefaultItem
(caseitem != null) ? _binder.BindExpression(caseitem.CaseVal) : null; (caseitem != null) ? _binder.BindExpression(caseitem.CaseVal) : null;
return new CaseBlock(caseValue); return WithNewOrdinal(new CaseBlock(caseValue));
} }
private BoundBlock/*!*/Connect(BoundBlock/*!*/source, BoundBlock/*!*/ifTarget, BoundBlock/*!*/elseTarget, Expression/*!*/condition) private BoundBlock/*!*/Connect(BoundBlock/*!*/source, BoundBlock/*!*/ifTarget, BoundBlock/*!*/elseTarget, Expression/*!*/condition)
@ -203,6 +239,23 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
return result; return result;
} }
/// <summary>
/// Gets new block index.
/// </summary>
private int NewOrdinal() => _index++;
private T WithNewOrdinal<T>(T block) where T : BoundBlock
{
block.Ordinal = NewOrdinal();
return block;
}
private T WithOpenScope<T>(T block) where T : BoundBlock
{
OpenScope(block);
return block;
}
#endregion #endregion
#region Declaration Statements #region Declaration Statements
@ -327,7 +380,7 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
var enumereeEdge = new ForeachEnumereeEdge(_current, move, _binder.BindExpression(x.Enumeree)); var enumereeEdge = new ForeachEnumereeEdge(_current, move, _binder.BindExpression(x.Enumeree));
// ContinueTarget: // ContinueTarget:
EnterBreakTarget(end, move); OpenBreakScope(end, move);
// ForeachMoveNextEdge : ConditionalEdge // ForeachMoveNextEdge : ConditionalEdge
var moveEdge = new ForeachMoveNextEdge(move, body, end, enumereeEdge, x.KeyVariable, x.ValueVariable); var moveEdge = new ForeachMoveNextEdge(move, body, end, enumereeEdge, x.KeyVariable, x.ValueVariable);
@ -337,16 +390,17 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
// Block // Block
// { x.Body } // { x.Body }
_current = body; _current = WithOpenScope(WithNewOrdinal(body));
VisitElement(x.Body); VisitElement(x.Body);
CloseScope();
// goto ContinueTarget; // goto ContinueTarget;
Connect(_current, move); Connect(_current, move);
// BreakTarget: // BreakTarget:
ExitBreakTarget(); CloseBreakScope();
// //
_current = end; _current = WithNewOrdinal(end);
} }
private void BuildForLoop(List<Expression> initExpr, List<Expression> condExpr, List<Expression> actionExpr, Statement/*!*/bodyStmt) private void BuildForLoop(List<Expression> initExpr, List<Expression> condExpr, List<Expression> actionExpr, Statement/*!*/bodyStmt)
@ -363,37 +417,42 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
var body = NewBlock(); var body = NewBlock();
var cond = hasConditions ? NewBlock() : body; var cond = hasConditions ? NewBlock() : body;
var action = hasActions ? NewBlock() : cond; var action = hasActions ? NewBlock() : cond;
EnterBreakTarget(end, action); OpenBreakScope(end, action);
// while (x.Codition) { // while (x.Codition) {
_current = Connect(_current, cond); _current = WithNewOrdinal(Connect(_current, cond));
if (hasConditions) if (hasConditions)
{ {
if (condExpr.Count > 1) if (condExpr.Count > 1)
condExpr.Take(condExpr.Count - 1).ForEach(expr => this.Add(new ExpressionStmt(expr.Span, expr))); condExpr.Take(condExpr.Count - 1).ForEach(expr => this.Add(new ExpressionStmt(expr.Span, expr)));
_current = Connect(_current, body, end, condExpr.LastOrDefault()); _current = WithNewOrdinal(Connect(_current, body, end, condExpr.LastOrDefault()));
} }
else else
{ {
_deadBlocks.Add(end); _deadBlocks.Add(end);
} }
OpenScope(_current);
// { x.Body } // { x.Body }
VisitElement(bodyStmt); VisitElement(bodyStmt);
// { x.Action } // { x.Action }
if (hasActions) if (hasActions)
{ {
_current = Connect(_current, action); _current = WithNewOrdinal(Connect(_current, action));
actionExpr.ForEach(expr => this.Add(new ExpressionStmt(expr.Span, expr))); actionExpr.ForEach(expr => this.Add(new ExpressionStmt(expr.Span, expr)));
} }
CloseScope();
// } // }
Connect(_current, cond); Connect(_current, cond);
// //
ExitBreakTarget(); CloseBreakScope();
// //
_current = end; _current = WithNewOrdinal(end);
} }
public override void VisitForStmt(ForStmt x) public override void VisitForStmt(ForStmt x)
@ -427,7 +486,7 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
? ((IntLiteral)x.Expression).Value ? ((IntLiteral)x.Expression).Value
: 1; : 1;
var brk = GetBreakTarget(level); var brk = GetBreakScope(level);
var target = (x.Type == JumpStmt.Types.Break) ? brk.BreakTarget : brk.ContinueTarget; var target = (x.Type == JumpStmt.Types.Break) ? brk.BreakTarget : brk.ContinueTarget;
if (target != null) if (target != null)
{ {
@ -466,26 +525,34 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
Debug.Assert(i != 0 && elseBlock != null); Debug.Assert(i != 0 && elseBlock != null);
var body = elseBlock; var body = elseBlock;
elseBlock = end; // last ConditionalStmt elseBlock = end; // last ConditionalStmt
_current = Connect(_current, body); _current = WithNewOrdinal(Connect(_current, body));
} }
OpenScope(_current);
VisitElement(cond.Statement); VisitElement(cond.Statement);
CloseScope();
Connect(_current, end); Connect(_current, end);
_current = elseBlock; _current = WithNewOrdinal(elseBlock);
} }
Debug.Assert(_current == end); Debug.Assert(_current == end);
_current.Ordinal = NewOrdinal();
} }
public override void VisitLabelStmt(LabelStmt x) public override void VisitLabelStmt(LabelStmt x)
{ {
var/*!*/label = GetLabelBlock(x.Name.Value); var/*!*/label = GetLabelBlock(x.Name.Value);
if ((label.Flags & ControlFlowGraph.LabelBlockFlags.Defined) != 0) if ((label.Flags & ControlFlowGraph.LabelBlockFlags.Defined) != 0)
{
label.Flags |= ControlFlowGraph.LabelBlockFlags.Redefined; // label was defined already label.Flags |= ControlFlowGraph.LabelBlockFlags.Redefined; // label was defined already
return; // ignore label redefinition
}
label.Flags |= ControlFlowGraph.LabelBlockFlags.Defined; // label is defined label.Flags |= ControlFlowGraph.LabelBlockFlags.Defined; // label is defined
label.LabelSpan = x.Span; label.LabelSpan = x.Span;
_current = Connect(_current, label.TargetBlock); _current = WithNewOrdinal(Connect(_current, label.TargetBlock));
Add(x); Add(x);
} }
@ -513,19 +580,23 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
// SwitchEdge // Connects _current to cases // SwitchEdge // Connects _current to cases
var edge = new SwitchEdge(_current, _binder.BindExpression(x.SwitchValue), cases.ToArray()); var edge = new SwitchEdge(_current, _binder.BindExpression(x.SwitchValue), cases.ToArray());
_current = cases[0]; _current = WithNewOrdinal(cases[0]);
EnterBreakTarget(end, end); // NOTE: inside switch, Continue ~ Break OpenBreakScope(end, end); // NOTE: inside switch, Continue ~ Break
for (int i = 0; i < cases.Count; i++) for (int i = 0; i < cases.Count; i++)
{ {
OpenScope(_current);
if (i < items.Length) if (i < items.Length)
items[i].Statements.ForEach(VisitElement); // any break will connect block to end items[i].Statements.ForEach(VisitElement); // any break will connect block to end
_current = Connect(_current, (i == cases.Count - 1) ? end : cases[i + 1]); CloseScope();
_current = WithNewOrdinal(Connect(_current, (i == cases.Count - 1) ? end : cases[i + 1]));
} }
ExitBreakTarget(); CloseBreakScope();
Debug.Assert(_current == end); Debug.Assert(_current == end);
} }
@ -588,29 +659,34 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
var edge = new TryCatchEdge(_current, body, catchBlocks, finallyBlock); var edge = new TryCatchEdge(_current, body, catchBlocks, finallyBlock);
// build try body // build try body
EnterTryTarget(edge); OpenTryScope(edge);
_current = body; OpenScope(body);
_current = WithNewOrdinal(body);
x.Statements.ForEach(VisitElement); x.Statements.ForEach(VisitElement);
ExitTryTarget(); CloseScope();
CloeTryScope();
_current = Connect(_current, finallyBlock ?? end); _current = Connect(_current, finallyBlock ?? end);
// built catches // built catches
for (int i = 0; i < catchBlocks.Length; i++) for (int i = 0; i < catchBlocks.Length; i++)
{ {
_current = catchBlocks[i]; _current = WithOpenScope(WithNewOrdinal(catchBlocks[i]));
x.Catches[i].Statements.ForEach(VisitElement); x.Catches[i].Statements.ForEach(VisitElement);
CloseScope();
_current = Connect(_current, finallyBlock ?? end); _current = Connect(_current, finallyBlock ?? end);
} }
// build finally // build finally
if (finallyBlock != null) if (finallyBlock != null)
{ {
_current = finallyBlock; _current = WithOpenScope(WithNewOrdinal(finallyBlock));
x.FinallyItem.Statements.ForEach(VisitElement); x.FinallyItem.Statements.ForEach(VisitElement);
CloseScope();
_current = Connect(_current, end); _current = Connect(_current, end);
} }
// _current == end // _current == end
_current.Ordinal = NewOrdinal();
} }
public override void VisitWhileStmt(WhileStmt x) public override void VisitWhileStmt(WhileStmt x)
@ -619,14 +695,16 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
{ {
var end = NewBlock(); var end = NewBlock();
var body = NewBlock(); var body = NewBlock();
EnterBreakTarget(end, body); OpenBreakScope(end, body);
_current = Connect(_current, body); _current = WithOpenScope(Connect(_current, body));
// do { // do {
VisitElement(x.Body); VisitElement(x.Body);
Connect(_current, body, end, x.CondExpr); Connect(_current, body, end, x.CondExpr);
// } while (x.CondExpr) // } while (x.CondExpr)
ExitBreakTarget(); CloseScope();
_current = end; CloseBreakScope();
_current = WithNewOrdinal(end);
} }
else if (x.LoopType == WhileStmt.Type.While) else if (x.LoopType == WhileStmt.Type.While)
{ {

Просмотреть файл

@ -85,6 +85,7 @@ namespace Pchp.CodeAnalysis.Semantics.Graph
internal SimpleEdge(BoundBlock source, BoundBlock target) internal SimpleEdge(BoundBlock source, BoundBlock target)
: base(source) : base(source)
{ {
Debug.Assert(source != target);
_target = target; _target = target;
Connect(source); Connect(source);
} }