@ -37,7 +37,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.CSharp.Syntax
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
{
public abstract class AstNode : AbstractAnnotatable , IFreez able , INode , ICloneable
public abstract partial class AstNode : AbstractAnnotatable , INode , ICloneable
{
{
// the Root role must be available when creating the null nodes, so we can't put it in the Roles class
// 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" , null ) ;
internal static readonly Role < AstNode ? > RootRole = new Role < AstNode ? > ( "Root" , null ) ;
@ -135,40 +135,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
// Flags, from least significant to most significant bits:
// Flags, from least significant to most significant bits:
// - Role.RoleIndexBits: role index
// - Role.RoleIndexBits: role index
// - 1 bit: IsFrozen
protected uint flags = RootRole . Index ;
protected uint flags = RootRole . Index ;
// Derived classes may also use a few bits,
// Derived classes may also use a few bits,
// for example Identifier uses 1 bit for IsVerbatim
// for example Identifier uses 1 bit for IsVerbatim
const uint roleIndexMask = ( 1 u < < Role . RoleIndexBits ) - 1 ;
const uint roleIndexMask = ( 1 u < < Role . RoleIndexBits ) - 1 ;
const uint frozenBit = 1 u < < Role . RoleIndexBits ;
protected const int AstNodeFlagsUsedBits = 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 {
public abstract NodeType NodeType {
get ;
get ;
@ -211,7 +183,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
throw new ArgumentNullException ( nameof ( value ) ) ;
throw new ArgumentNullException ( nameof ( value ) ) ;
if ( ! value . IsValid ( this ) )
if ( ! value . IsValid ( this ) )
throw new ArgumentException ( "This node is not valid in the new role." ) ;
throw new ArgumentException ( "This node is not valid in the new role." ) ;
ThrowIfFrozen ( ) ;
SetRole ( value ) ;
SetRole ( value ) ;
}
}
}
}
@ -382,13 +353,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
throw new ArgumentNullException ( nameof ( role ) ) ;
throw new ArgumentNullException ( nameof ( role ) ) ;
if ( child = = null | | child . IsNull )
if ( child = = null | | child . IsNull )
return ;
return ;
ThrowIfFrozen ( ) ;
if ( child = = this )
if ( child = = this )
throw new ArgumentException ( "Cannot add a node to itself as a child." , nameof ( child ) ) ;
throw new ArgumentException ( "Cannot add a node to itself as a child." , nameof ( child ) ) ;
if ( child . parent ! = null )
if ( child . parent ! = null )
throw new ArgumentException ( "Node is already used in another tree." , nameof ( child ) ) ;
throw new ArgumentException ( "Node is already used in another tree." , nameof ( child ) ) ;
if ( child . IsFrozen )
throw new ArgumentException ( "Cannot add a frozen node." , nameof ( child ) ) ;
AddChildUnsafe ( child , role ) ;
AddChildUnsafe ( child , role ) ;
}
}
@ -396,13 +364,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
{
if ( child = = null | | child . IsNull )
if ( child = = null | | child . IsNull )
return ;
return ;
ThrowIfFrozen ( ) ;
if ( child = = this )
if ( child = = this )
throw new ArgumentException ( "Cannot add a node to itself as a child." , nameof ( child ) ) ;
throw new ArgumentException ( "Cannot add a node to itself as a child." , nameof ( child ) ) ;
if ( child . parent ! = null )
if ( child . parent ! = null )
throw new ArgumentException ( "Node is already used in another tree." , nameof ( child ) ) ;
throw new ArgumentException ( "Node is already used in another tree." , nameof ( child ) ) ;
if ( child . IsFrozen )
throw new ArgumentException ( "Cannot add a frozen node." , nameof ( child ) ) ;
AddChildUnsafe ( child , child . Role ) ;
AddChildUnsafe ( child , child . Role ) ;
}
}
@ -437,11 +402,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if ( child = = null | | child . IsNull )
if ( child = = null | | child . IsNull )
return ;
return ;
ThrowIfFrozen ( ) ;
if ( child . parent ! = null )
if ( child . parent ! = null )
throw new ArgumentException ( "Node is already used in another tree." , nameof ( child ) ) ;
throw new ArgumentException ( "Node is already used in another tree." , nameof ( child ) ) ;
if ( child . IsFrozen )
throw new ArgumentException ( "Cannot add a frozen node." , nameof ( child ) ) ;
if ( nextSibling . parent ! = this )
if ( nextSibling . parent ! = this )
throw new ArgumentException ( "NextSibling is not a child of this node." , nameof ( nextSibling ) ) ;
throw new ArgumentException ( "NextSibling is not a child of this node." , nameof ( nextSibling ) ) ;
// No need to test for "Cannot add children to null nodes",
// No need to test for "Cannot add children to null nodes",
@ -481,7 +443,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
{
if ( parent ! = null )
if ( parent ! = null )
{
{
ThrowIfFrozen ( ) ;
if ( prevSibling ! = null )
if ( prevSibling ! = null )
{
{
Debug . Assert ( prevSibling . nextSibling = = this ) ;
Debug . Assert ( prevSibling . nextSibling = = this ) ;
@ -524,7 +485,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
{
throw new InvalidOperationException ( this . IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node" ) ;
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,
// Because this method doesn't statically check the new node's type with the role,
// we perform a runtime test:
// we perform a runtime test:
if ( ! this . Role . IsValid ( newNode ) )
if ( ! this . Role . IsValid ( newNode ) )
@ -545,9 +505,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
throw new ArgumentException ( "Node is already used in another tree." , nameof ( newNode ) ) ;
throw new ArgumentException ( "Node is already used in another tree." , nameof ( newNode ) ) ;
}
}
}
}
if ( newNode . IsFrozen )
throw new ArgumentException ( "Cannot add a frozen node." , nameof ( newNode ) ) ;
newNode . parent = parent ;
newNode . parent = parent ;
newNode . SetRole ( this . Role ) ;
newNode . SetRole ( this . Role ) ;
newNode . prevSibling = prevSibling ;
newNode . prevSibling = prevSibling ;
@ -623,7 +580,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
copy . lastChild = null ;
copy . lastChild = null ;
copy . prevSibling = null ;
copy . prevSibling = null ;
copy . nextSibling = null ;
copy . nextSibling = null ;
copy . flags & = ~ frozenBit ; // unfreeze the copy
// Then perform a deep copy:
// Then perform a deep copy:
for ( AstNode ? cur = firstChild ; cur ! = null ; cur = cur . nextSibling )
for ( AstNode ? cur = firstChild ; cur ! = null ; cur = cur . nextSibling )