|
|
|
|
@ -33,12 +33,10 @@ using System.Threading;
@@ -33,12 +33,10 @@ using System.Threading;
|
|
|
|
|
|
|
|
|
|
namespace ICSharpCode.NRefactory.CSharp |
|
|
|
|
{ |
|
|
|
|
public abstract class AstNode : AbstractAnnotatable, PatternMatching.INode |
|
|
|
|
public abstract class AstNode : AbstractAnnotatable, ICSharpCode.NRefactory.TypeSystem.IFreezable, PatternMatching.INode |
|
|
|
|
{ |
|
|
|
|
/// <summary>
|
|
|
|
|
/// Root of an abstract syntax tree.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly Role<AstNode> Root = new Role<AstNode> ("Root"); |
|
|
|
|
// the Root role must be available when creating the null nodes, so we can't put it in the Roles class
|
|
|
|
|
internal static readonly Role<AstNode> RootRole = new Role<AstNode> ("Root"); |
|
|
|
|
|
|
|
|
|
#region Null
|
|
|
|
|
public static readonly AstNode Null = new NullAstNode (); |
|
|
|
|
@ -129,7 +127,42 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -129,7 +127,42 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
AstNode nextSibling; |
|
|
|
|
AstNode firstChild; |
|
|
|
|
AstNode lastChild; |
|
|
|
|
Role role = Root; |
|
|
|
|
|
|
|
|
|
// Flags, from least significant to most significant bits:
|
|
|
|
|
// - Role.RoleIndexBits: role index
|
|
|
|
|
// - 1 bit: IsFrozen
|
|
|
|
|
protected uint flags = RootRole.Index; |
|
|
|
|
// Derived classes may also use a few bits,
|
|
|
|
|
// for example Identifier uses 1 bit for IsVerbatim
|
|
|
|
|
|
|
|
|
|
const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1; |
|
|
|
|
const uint frozenBit = 1u << Role.RoleIndexBits; |
|
|
|
|
protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits + 1; |
|
|
|
|
|
|
|
|
|
protected AstNode() |
|
|
|
|
{ |
|
|
|
|
if (IsNull) |
|
|
|
|
Freeze(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public bool IsFrozen { |
|
|
|
|
get { return (flags & frozenBit) != 0; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void Freeze() |
|
|
|
|
{ |
|
|
|
|
if (!IsFrozen) { |
|
|
|
|
for (AstNode child = firstChild; child != null; child = child.nextSibling) |
|
|
|
|
child.Freeze(); |
|
|
|
|
flags |= frozenBit; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected void ThrowIfFrozen() |
|
|
|
|
{ |
|
|
|
|
if (IsFrozen) |
|
|
|
|
throw new InvalidOperationException("Cannot mutate frozen " + GetType().Name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public abstract NodeType NodeType { |
|
|
|
|
get; |
|
|
|
|
@ -176,16 +209,24 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -176,16 +209,24 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Role Role { |
|
|
|
|
get { return role; } |
|
|
|
|
get { |
|
|
|
|
return Role.GetByIndex(flags & roleIndexMask); |
|
|
|
|
} |
|
|
|
|
set { |
|
|
|
|
if (value == null) |
|
|
|
|
throw new ArgumentNullException("value"); |
|
|
|
|
if (!value.IsValid(this)) |
|
|
|
|
throw new ArgumentException("This node is not valid in the new role."); |
|
|
|
|
role = value; |
|
|
|
|
ThrowIfFrozen(); |
|
|
|
|
SetRole(value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SetRole(Role role) |
|
|
|
|
{ |
|
|
|
|
flags = (flags & ~roleIndexMask) | role.Index; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public AstNode NextSibling { |
|
|
|
|
get { return nextSibling; } |
|
|
|
|
} |
|
|
|
|
@ -258,8 +299,9 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -258,8 +299,9 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
{ |
|
|
|
|
if (role == null) |
|
|
|
|
throw new ArgumentNullException ("role"); |
|
|
|
|
uint roleIndex = role.Index; |
|
|
|
|
for (var cur = firstChild; cur != null; cur = cur.nextSibling) { |
|
|
|
|
if (cur.role == role) |
|
|
|
|
if ((cur.flags & roleIndexMask) == roleIndex) |
|
|
|
|
return (T)cur; |
|
|
|
|
} |
|
|
|
|
return role.NullObject; |
|
|
|
|
@ -285,10 +327,11 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -285,10 +327,11 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
throw new ArgumentNullException ("role"); |
|
|
|
|
if (child == null || child.IsNull) |
|
|
|
|
return; |
|
|
|
|
if (this.IsNull) |
|
|
|
|
throw new InvalidOperationException ("Cannot add children to null nodes"); |
|
|
|
|
ThrowIfFrozen(); |
|
|
|
|
if (child.parent != null) |
|
|
|
|
throw new ArgumentException ("Node is already used in another tree.", "child"); |
|
|
|
|
if (child.IsFrozen) |
|
|
|
|
throw new ArgumentException ("Cannot add a frozen node.", "child"); |
|
|
|
|
AddChildUnsafe (child, role); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -298,7 +341,7 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -298,7 +341,7 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
void AddChildUnsafe (AstNode child, Role role) |
|
|
|
|
{ |
|
|
|
|
child.parent = this; |
|
|
|
|
child.role = role; |
|
|
|
|
child.SetRole(role); |
|
|
|
|
if (firstChild == null) { |
|
|
|
|
lastChild = firstChild = child; |
|
|
|
|
} else { |
|
|
|
|
@ -319,8 +362,11 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -319,8 +362,11 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
|
|
|
|
|
if (child == null || child.IsNull) |
|
|
|
|
return; |
|
|
|
|
ThrowIfFrozen(); |
|
|
|
|
if (child.parent != null) |
|
|
|
|
throw new ArgumentException ("Node is already used in another tree.", "child"); |
|
|
|
|
if (child.IsFrozen) |
|
|
|
|
throw new ArgumentException ("Cannot add a frozen node.", "child"); |
|
|
|
|
if (nextSibling.parent != this) |
|
|
|
|
throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling"); |
|
|
|
|
// No need to test for "Cannot add children to null nodes",
|
|
|
|
|
@ -331,7 +377,7 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -331,7 +377,7 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
void InsertChildBeforeUnsafe (AstNode nextSibling, AstNode child, Role role) |
|
|
|
|
{ |
|
|
|
|
child.parent = this; |
|
|
|
|
child.role = role; |
|
|
|
|
child.SetRole(role); |
|
|
|
|
child.nextSibling = nextSibling; |
|
|
|
|
child.prevSibling = nextSibling.prevSibling; |
|
|
|
|
|
|
|
|
|
@ -356,6 +402,7 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -356,6 +402,7 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
public void Remove () |
|
|
|
|
{ |
|
|
|
|
if (parent != null) { |
|
|
|
|
ThrowIfFrozen(); |
|
|
|
|
if (prevSibling != null) { |
|
|
|
|
Debug.Assert (prevSibling.nextSibling == this); |
|
|
|
|
prevSibling.nextSibling = nextSibling; |
|
|
|
|
@ -390,10 +437,11 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -390,10 +437,11 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
if (parent == null) { |
|
|
|
|
throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); |
|
|
|
|
} |
|
|
|
|
ThrowIfFrozen(); |
|
|
|
|
// Because this method doesn't statically check the new node's type with the role,
|
|
|
|
|
// we perform a runtime test:
|
|
|
|
|
if (!role.IsValid (newNode)) { |
|
|
|
|
throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, role.ToString ()), "newNode"); |
|
|
|
|
if (!this.Role.IsValid (newNode)) { |
|
|
|
|
throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, this.Role.ToString ()), "newNode"); |
|
|
|
|
} |
|
|
|
|
if (newNode.parent != null) { |
|
|
|
|
// newNode is used within this tree?
|
|
|
|
|
@ -405,9 +453,11 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -405,9 +453,11 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
throw new ArgumentException ("Node is already used in another tree.", "newNode"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (newNode.IsFrozen) |
|
|
|
|
throw new ArgumentException ("Cannot add a frozen node.", "newNode"); |
|
|
|
|
|
|
|
|
|
newNode.parent = parent; |
|
|
|
|
newNode.role = role; |
|
|
|
|
newNode.SetRole(this.Role); |
|
|
|
|
newNode.prevSibling = prevSibling; |
|
|
|
|
newNode.nextSibling = nextSibling; |
|
|
|
|
if (parent != null) { |
|
|
|
|
@ -440,7 +490,7 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -440,7 +490,7 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
} |
|
|
|
|
AstNode oldParent = parent; |
|
|
|
|
AstNode oldSuccessor = nextSibling; |
|
|
|
|
Role oldRole = role; |
|
|
|
|
Role oldRole = this.Role; |
|
|
|
|
Remove (); |
|
|
|
|
AstNode replacement = replaceFunction (this); |
|
|
|
|
if (oldSuccessor != null && oldSuccessor.parent != oldParent) |
|
|
|
|
@ -473,10 +523,11 @@ namespace ICSharpCode.NRefactory.CSharp
@@ -473,10 +523,11 @@ namespace ICSharpCode.NRefactory.CSharp
|
|
|
|
|
copy.lastChild = null; |
|
|
|
|
copy.prevSibling = null; |
|
|
|
|
copy.nextSibling = null; |
|
|
|
|
copy.flags &= ~frozenBit; // unfreeze the copy
|
|
|
|
|
|
|
|
|
|
// Then perform a deep copy:
|
|
|
|
|
for (AstNode cur = firstChild; cur != null; cur = cur.nextSibling) { |
|
|
|
|
copy.AddChildUnsafe (cur.Clone (), cur.role); |
|
|
|
|
copy.AddChildUnsafe (cur.Clone (), cur.Role); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Finally, clone the annotation, if necessary
|
|
|
|
|
|