2016-03-30 14:20:52 -07:00
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
//
// This file contains all of the publicly visible parts of the PowerShell abstract syntax tree.
// Any private/internal methods or properties are found in the file AstCompile.cs.
//
using System.Collections ;
using System.Diagnostics.CodeAnalysis ;
using System.Globalization ;
using System.Collections.ObjectModel ;
using System.Collections.Generic ;
using System.Linq ;
using System.Linq.Expressions ;
using System.Reflection ;
using System.Text ;
using System.Threading ;
using Microsoft.PowerShell ;
using Microsoft.PowerShell.Commands ;
#pragma warning disable 1634 , 1691 // Stops compiler from warning about unknown warnings
namespace System.Management.Automation.Language
{
using KeyValuePair = Tuple < ExpressionAst , StatementAst >;
using IfClause = Tuple < PipelineBaseAst , StatementBlockAst >;
using SwitchClause = Tuple < ExpressionAst , StatementBlockAst >;
using System.Runtime.CompilerServices ;
using System.Reflection.Emit ;
internal interface ISupportsAssignment
{
IAssignableValue GetAssignableValue ();
}
internal interface IAssignableValue
{
/// <summary>
/// GetValue is only called for pre/post increment/decrement or for read/modify/write assignment operators (+=, -=, etc.)
/// It returns the expressions that holds the value of the ast. It may append the exprs or temps lists if the return
/// value relies on temps and other expressions.
/// </summary>
Expression GetValue ( Compiler compiler , List < Expression > exprs , List < ParameterExpression > temps );
/// <summary>
/// SetValue is called to set the result of an assignment (=) or to write back the result of
/// a pre/post increment/decrement. It needs to use potentially cached temps if GetValue was called first.
/// </summary>
Expression SetValue ( Compiler compiler , Expression rhs );
}
internal interface IParameterMetadataProvider
{
RuntimeDefinedParameterDictionary GetParameterMetadata ( bool automaticPositions , ref bool usesCmdletBinding );
IEnumerable < Attribute > GetScriptBlockAttributes ();
bool UsesCmdletBinding ();
ReadOnlyCollection < ParameterAst > Parameters { get ; }
ScriptBlockAst Body { get ; }
#region Remoting / Invoke Command
PowerShell GetPowerShell ( ExecutionContext context , Dictionary < string , object > variables , bool isTrustedInput ,
bool filterNonUsingVariables , bool? createLocalScope , params object [] args );
string GetWithInputHandlingForInvokeCommand ();
/// <summary>
/// Return value is Tuple[paramText, scriptBlockText]
/// </summary>
Tuple < string , string > GetWithInputHandlingForInvokeCommandWithUsingExpression ( Tuple < List < VariableExpressionAst >, string > usingVariablesTuple );
#endregion Remoting / Invoke Command
}
/// <summary>
/// The abstract base class for all PowerShell abstract syntax tree nodes.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
public abstract class Ast
{
/// <summary>
/// Initialize the common fields of an ast.
/// </summary>
/// <param name="extent">The extent of the expression.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
protected Ast ( IScriptExtent extent )
{
if ( extent == null )
{
throw PSTraceSource . NewArgumentNullException ( "extent" );
}
this . Extent = extent ;
}
/// <summary>
/// The extent in the source this ast represents.
/// </summary>
public IScriptExtent Extent { get ; private set ; }
/// <summary>
/// The parent tree for this node.
/// </summary>
public Ast Parent { get ; private set ; }
/// <summary>
/// Visit the Ast using a visitor that can choose how the tree traversal is performed. This visit method is
/// for advanced uses of the visitor pattern where an <see cref="AstVisitor"/> is insufficient.
/// </summary>
/// <param name="astVisitor">The visitor</param>
/// <returns>Returns the value returned by the visitor.</returns>
public object Visit ( ICustomAstVisitor astVisitor )
{
if ( astVisitor == null )
{
throw PSTraceSource . NewArgumentNullException ( "astVisitor" );
}
return this . Accept ( astVisitor );
}
/// <summary>
/// Visit each node in the Ast, calling the methods in <paramref name="astVisitor"/> for each node in the ast.
/// </summary>
/// <param name="astVisitor">The visitor</param>
public void Visit ( AstVisitor astVisitor )
{
if ( astVisitor == null )
{
throw PSTraceSource . NewArgumentNullException ( "astVisitor" );
}
this . InternalVisit ( astVisitor );
}
/// <summary>
/// Traverse the entire Ast, returning all nodes in the tree for which <paramref name="predicate"/> returns true.
/// </summary>
/// <param name="predicate">The predicate function.</param>
/// <param name="searchNestedScriptBlocks">Search nested functions and script block expressions.</param>
/// <returns>A possibly empty collection of matching Ast nodes.</returns>
public IEnumerable < Ast > FindAll ( Func < Ast , bool > predicate , bool searchNestedScriptBlocks )
{
if ( predicate == null )
{
throw PSTraceSource . NewArgumentNullException ( "predicate" );
}
return AstSearcher . FindAll ( this , predicate , searchNestedScriptBlocks );
}
/// <summary>
/// Traverse the entire Ast, returning the first node in the tree for which <paramref name="predicate"/> returns true.
/// </summary>
/// <param name="predicate">The predicate</param>
/// <param name="searchNestedScriptBlocks">Search nested functions and script block expressions.</param>
/// <returns>The first matching node, or null if there is no match.</returns>
public Ast Find ( Func < Ast , bool > predicate , bool searchNestedScriptBlocks )
{
if ( predicate == null )
{
throw PSTraceSource . NewArgumentNullException ( "predicate" );
}
return AstSearcher . FindFirst ( this , predicate , searchNestedScriptBlocks );
}
/// <summary>
/// Formats the ast and returns a string.
/// </summary>
public override string ToString ()
{
return Extent . Text ;
}
/// <summary>
/// Duplicates the AST, allowing it to be composed into other ASTs.
/// </summary>
/// <returns>A copy of the AST, with the link to the previous parent removed.</returns>
public abstract Ast Copy ();
/// <summary>
/// Constructs the resultant object from the AST and returns it if it is safe.
/// </summary>
/// <returns>The object represented by the AST as a safe object</returns>
/// <exception cref="InvalidOperationException">
/// If <paramref name="extent"/> is deemed unsafe
/// </exception>
public object SafeGetValue ()
{
try
{
ExecutionContext context = null ;
if ( System . Management . Automation . Runspaces . Runspace . DefaultRunspace != null )
{
context = System . Management . Automation . Runspaces . Runspace . DefaultRunspace . ExecutionContext ;
}
return GetSafeValueVisitor . GetSafeValue ( this , context , GetSafeValueVisitor . SafeValueContext . Default );
}
catch
{
throw new InvalidOperationException ( String . Format ( CultureInfo . InvariantCulture , AutomationExceptions . CantConvertWithDynamicExpression , this . Extent . Text ));
}
}
/// <summary>
/// Copy a collection of AST elements
/// </summary>
/// <typeparam name="T">The actual AST type</typeparam>
/// <param name="elements">Collection of ASTs</param>
/// <returns></returns>
static internal T [] CopyElements < T >( ReadOnlyCollection < T > elements ) where T : Ast
{
if ( elements == null || elements . Count == 0 ) { return null ; }
var result = new T [ elements . Count ];
for ( int i = 0 ; i < result . Count (); i ++)
{
result [ i ] = ( T ) elements [ i ]. Copy ();
}
return result ;
}
/// <summary>
/// Copy a single AST element
/// </summary>
/// <typeparam name="T">The actual AST type</typeparam>
/// <param name="element">An AST instance</param>
/// <returns></returns>
static internal T CopyElement < T >( T element ) where T : Ast
{
if ( element == null ) { return null ; }
return ( T ) element . Copy ();
}
// Should be protected AND internal, but C# doesn't support that.
internal void SetParents < T >( ReadOnlyCollection < T > children )
where T : Ast
{
for ( int index = 0 ; index < children . Count ; index ++)
{
var child = children [ index ];
SetParent ( child );
}
}
// Should be protected AND internal, but C# doesn't support that.
internal void SetParents < T1 , T2 >( ReadOnlyCollection < Tuple < T1 , T2 >> children )
where T1 : Ast
where T2 : Ast
{
for ( int index = 0 ; index < children . Count ; index ++)
{
var child = children [ index ];
SetParent ( child . Item1 );
SetParent ( child . Item2 );
}
}
// Should be protected AND internal, but C# doesn't support that.
internal void SetParent ( Ast child )
{
if ( child . Parent != null )
{
throw new InvalidOperationException ( ParserStrings . AstIsReused );
}
Diagnostics . Assert ( child . Parent == null , "Parent can only be set once" );
child . Parent = this ;
}
internal void ClearParent ()
{
this . Parent = null ;
}
internal abstract object Accept ( ICustomAstVisitor visitor );
internal abstract AstVisitAction InternalVisit ( AstVisitor visitor );
internal abstract IEnumerable < PSTypeName > GetInferredType ( CompletionContext context );
internal static PSTypeName [] EmptyPSTypeNameArray = Utils . EmptyArray < PSTypeName >();
internal bool IsInWorkflow ()
{
// Scan up the AST's parents, looking for a script block that is either
// a workflow, or has a job definition attribute.
// Stop scanning when we encounter a FunctionDefinitionAst
Ast current = this ;
bool stopScanning = false ;
while ( current != null && ! stopScanning )
{
ScriptBlockAst scriptBlock = current as ScriptBlockAst ;
if ( scriptBlock != null )
{
// See if this uses the workflow keyword
FunctionDefinitionAst functionDefinition = scriptBlock . Parent as FunctionDefinitionAst ;
if (( functionDefinition != null ))
{
stopScanning = true ;
if ( functionDefinition . IsWorkflow ) { return true ; }
}
}
CommandAst commandAst = current as CommandAst ;
if ( commandAst != null &&
String . Equals ( TokenKind . InlineScript . Text (), commandAst . GetCommandName (), StringComparison . OrdinalIgnoreCase ) &&
this != commandAst )
{
return false ;
}
current = current . Parent ;
}
return false ;
}
internal bool HasSuspiciousContent { get ; set ; }
#region Search Ancestor Ast
internal static ConfigurationDefinitionAst GetAncestorConfigurationDefinitionAstAndDynamicKeywordStatementAst (
Ast ast ,
out DynamicKeywordStatementAst keywordAst )
{
ConfigurationDefinitionAst configAst = null ;
keywordAst = GetAncestorAst < DynamicKeywordStatementAst >( ast );
configAst = ( keywordAst != null ) ? GetAncestorAst < ConfigurationDefinitionAst >( keywordAst ) : GetAncestorAst < ConfigurationDefinitionAst >( ast );
return configAst ;
}
internal static HashtableAst GetAncestorHashtableAst ( Ast ast , out Ast lastChildOfHashtable )
{
HashtableAst hashtableAst = null ;
lastChildOfHashtable = null ;
while ( ast != null )
{
hashtableAst = ast as HashtableAst ;
if ( hashtableAst != null )
break ;
lastChildOfHashtable = ast ;
ast = ast . Parent ;
}
return hashtableAst ;
}
internal static TypeDefinitionAst GetAncestorTypeDefinitionAst ( Ast ast )
{
TypeDefinitionAst typeDefinitionAst = null ;
while ( ast != null )
{
typeDefinitionAst = ast as TypeDefinitionAst ;
if ( typeDefinitionAst != null )
break ;
// Nested function isn't really a member of the type so stop looking
// Anonymous script blocks are though
var functionDefinitionAst = ast as FunctionDefinitionAst ;
if ( functionDefinitionAst != null && !( functionDefinitionAst . Parent is FunctionMemberAst ))
break ;
ast = ast . Parent ;
}
return typeDefinitionAst ;
}
/// <summary>
/// Get ancestor Ast of the given type of the given ast
/// </summary>
/// <param name="ast"></param>
/// <returns></returns>
internal static T GetAncestorAst < T >( Ast ast ) where T : Ast
{
T targetAst = null ;
var parent = ast ;
while ( parent != null )
{
targetAst = parent as T ;
if ( targetAst != null )
break ;
parent = parent . Parent ;
}
return targetAst ;
}
#endregion
}
// A dummy class to hold an extent for open/close curlies so we can step in the debugger.
// This Ast is never produced by the parser, only from the compiler.
internal class SequencePointAst : Ast
{
public SequencePointAst ( IScriptExtent extent )
: base ( extent )
{
}
/// <summary>
/// Copy the SequencePointAst instance
/// </summary>
public override Ast Copy ()
{
Diagnostics . Assert ( false , "code should be unreachable" );
return null ;
}
internal override object Accept ( ICustomAstVisitor visitor )
{
Diagnostics . Assert ( false , "code should be unreachable" );
return null ;
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
Diagnostics . Assert ( false , "code should be unreachable" );
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
Diagnostics . Assert ( false , "code should be unreachable" );
return Ast . EmptyPSTypeNameArray ;
}
}
/// <summary>
/// A placeholder statement used when there are syntactic errors in the source script.
/// </summary>
public class ErrorStatementAst : PipelineBaseAst
{
internal ErrorStatementAst ( IScriptExtent extent , IEnumerable < Ast > nestedAsts = null )
: base ( extent )
{
if ( nestedAsts != null && nestedAsts . Any ())
{
NestedAst = new ReadOnlyCollection < Ast >( nestedAsts . ToArray ());
SetParents ( NestedAst );
}
}
internal ErrorStatementAst ( IScriptExtent extent , Token kind , IEnumerable < Ast > nestedAsts = null )
: base ( extent )
{
if ( kind == null )
{
throw PSTraceSource . NewArgumentNullException ( "kind" );
}
Kind = kind ;
if ( nestedAsts != null && nestedAsts . Any ())
{
NestedAst = new ReadOnlyCollection < Ast >( nestedAsts . ToArray ());
SetParents ( NestedAst );
}
}
internal ErrorStatementAst ( IScriptExtent extent , Token kind , IEnumerable < KeyValuePair < string , Tuple < Token , Ast >>> flags , IEnumerable < Ast > conditions , IEnumerable < Ast > bodies )
: base ( extent )
{
if ( kind == null )
{
throw PSTraceSource . NewArgumentNullException ( "kind" );
}
Kind = kind ;
if ( flags != null && flags . Any ())
{
Flags = new Dictionary < string , Tuple < Token , Ast >>( StringComparer . OrdinalIgnoreCase );
foreach ( KeyValuePair < string , Tuple < Token , Ast >> entry in flags )
{
if ( Flags . ContainsKey ( entry . Key ))
continue ;
Flags . Add ( entry . Key , entry . Value );
if ( entry . Value . Item2 != null )
{
SetParent ( entry . Value . Item2 );
}
}
}
if ( conditions != null && conditions . Any ())
{
Conditions = new ReadOnlyCollection < Ast >( conditions . ToArray ());
SetParents ( Conditions );
}
if ( bodies != null && bodies . Any ())
{
Bodies = new ReadOnlyCollection < Ast >( bodies . ToArray ());
SetParents ( Bodies );
}
}
/// <summary>
/// Indicate the kind of the ErrorStatement. e.g. Kind == Switch means that this error statment is generated
/// when parsing a switch statement.
/// </summary>
public Token Kind { get ; private set ; }
/// <summary>
/// The flags specifid and their value. The value is null if it's not specified.
/// e.g. switch -regex -file c:\demo.txt ---> regex -- null
/// file -- { c:\demo.txt }
/// </summary>
/// TODO, Changing this to an IDictionary because ReadOnlyDictionary is available only in .NET 4.5
/// This is a temporary workaround and will be fixed later. Tracked by Win8: 354135
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public Dictionary < string , Tuple < Token , Ast >> Flags { get ; private set ; }
/// <summary>
/// The conditions specified.
/// </summary>
public ReadOnlyCollection < Ast > Conditions { get ; private set ; }
/// <summary>
/// The bodies specified.
/// </summary>
public ReadOnlyCollection < Ast > Bodies { get ; private set ; }
/// <summary>
/// Sometimes a valid ast is parsed successfully within the extent that this error statement represents. Those
/// asts are contained in this collection. This collection may contain other error asts. This collection may
/// be null when no asts were successfully constructed within the extent of this error ast.
/// </summary>
public ReadOnlyCollection < Ast > NestedAst { get ; private set ; }
/// <summary>
/// Copy the ErrorStatementAst instance
/// </summary>
public override Ast Copy ()
{
if ( this . Kind == null )
{
var newNestedAst = CopyElements ( this . NestedAst );
return new ErrorStatementAst ( this . Extent , newNestedAst );
}
else if ( Flags != null || Conditions != null || Bodies != null )
{
var newConditions = CopyElements ( this . Conditions );
var newBodies = CopyElements ( this . Bodies );
Dictionary < string , Tuple < Token , Ast >> newFlags = null ;
if ( this . Flags != null )
{
newFlags = new Dictionary < string , Tuple < Token , Ast >>( StringComparer . OrdinalIgnoreCase );
foreach ( KeyValuePair < string , Tuple < Token , Ast >> entry in this . Flags )
{
var newAst = CopyElement ( entry . Value . Item2 );
newFlags . Add ( entry . Key , new Tuple < Token , Ast >( entry . Value . Item1 , newAst ));
}
}
return new ErrorStatementAst ( this . Extent , this . Kind , newFlags , newConditions , newBodies );
}
else
{
var newNestedAst = CopyElements ( this . NestedAst );
return new ErrorStatementAst ( this . Extent , this . Kind , newNestedAst );
}
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Conditions . Concat ( Bodies ). Concat ( NestedAst ). SelectMany ( nestedAst => nestedAst . GetInferredType ( context ));
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitErrorStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitErrorStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && NestedAst != null )
{
for ( int index = 0 ; index < NestedAst . Count ; index ++)
{
var ast = NestedAst [ index ];
action = ast . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue && Flags != null )
{
foreach ( var tuple in Flags . Values )
{
if ( tuple . Item2 == null )
continue ;
action = tuple . Item2 . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue && Conditions != null )
{
for ( int index = 0 ; index < Conditions . Count ; index ++)
{
var ast = Conditions [ index ];
action = ast . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue && Bodies != null )
{
for ( int index = 0 ; index < Bodies . Count ; index ++)
{
var ast = Bodies [ index ];
action = ast . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// A placeholder expression used when there are syntactic errors in the source script.
/// </summary>
public class ErrorExpressionAst : ExpressionAst
{
internal ErrorExpressionAst ( IScriptExtent extent , IEnumerable < Ast > nestedAsts = null )
: base ( extent )
{
if ( nestedAsts != null && nestedAsts . Any ())
{
NestedAst = new ReadOnlyCollection < Ast >( nestedAsts . ToArray ());
SetParents ( NestedAst );
}
}
/// <summary>
/// Sometimes a valid ast is parsed successfully within the extent that this error expression represents. Those
/// asts are contained in this collection. This collection may contain other error asts. This collection may
/// be null when no asts were successfully constructed within the extent of this error ast.
/// </summary>
public ReadOnlyCollection < Ast > NestedAst { get ; private set ; }
/// <summary>
/// Copy the ErrorExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newNestedAst = CopyElements ( this . NestedAst );
return new ErrorExpressionAst ( this . Extent , newNestedAst );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return NestedAst . SelectMany ( nestedAst => nestedAst . GetInferredType ( context ));
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitErrorExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitErrorExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && NestedAst != null )
{
for ( int index = 0 ; index < NestedAst . Count ; index ++)
{
var ast = NestedAst [ index ];
action = ast . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
#region Script Blocks
/// <summary>
///
/// </summary>
public class ScriptRequirements
{
internal static readonly ReadOnlyCollection < PSSnapInSpecification > EmptySnapinCollection =
Utils . EmptyReadOnlyCollection < PSSnapInSpecification >();
internal static readonly ReadOnlyCollection < string > EmptyAssemblyCollection =
Utils . EmptyReadOnlyCollection < string >();
internal static readonly ReadOnlyCollection < ModuleSpecification > EmptyModuleCollection =
Utils . EmptyReadOnlyCollection < ModuleSpecification >();
2016-03-31 17:27:44 -07:00
internal static readonly ReadOnlyCollection < string > EmptyEditionCollection =
Utils . EmptyReadOnlyCollection < string >();
2016-03-30 14:20:52 -07:00
/// <summary>
/// The application id this script requires, specified like:
/// <code>#requires -Shellid Shell</code>
/// If no application id has been specified, this property is null.
/// </summary>
public string RequiredApplicationId { get ; internal set ; }
/// <summary>
/// The PowerShell version this script requires, specified like:
/// <code>#requires -Version 3</code>
/// If no version has been specified, this property is null.
/// </summary>
public Version RequiredPSVersion { get ; internal set ; }
/// <summary>
/// The PowerShell Edition this script requires, specified like:
/// <code>#requires -PSEdition Desktop</code>
2016-03-31 17:27:44 -07:00
/// If no PSEdition has been specified, this property is an empty collection.
2016-03-30 14:20:52 -07:00
/// </summary>
2016-03-31 17:27:44 -07:00
public ReadOnlyCollection < string > RequiredPSEditions { get ; internal set ; }
2016-03-30 14:20:52 -07:00
/// <summary>
/// The modules this script requires, specified like:
/// <code>#requires -Module NetAdapter</code>
/// <code>#requires -Module @{Name="NetAdapter"; Version="1.0.0.0"}</code>
/// If no modules are required, this property is an empty collection.
/// </summary>
public ReadOnlyCollection < ModuleSpecification > RequiredModules { get ; internal set ; }
/// <summary>
/// The snapins this script requires, specified like:
/// <code>#requires -PSSnapin Snapin</code>
/// <code>#requires -PSSnapin Snapin -Version 2</code>
/// If no snapins are required, this property is an empty collection.
/// </summary>
public ReadOnlyCollection < PSSnapInSpecification > RequiresPSSnapIns { get ; internal set ; }
/// <summary>
/// The aseemblies this script requires, specified like:
/// <code>#requires -Assembly path\to\foo.dll</code>
/// <code>#requires -Assembly "System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"</code>
/// If no assemblies are required, this property is an empty collection.
/// </summary>
public ReadOnlyCollection < string > RequiredAssemblies { get ; internal set ; }
/// <summary>
/// Specifies if this script requires elevated privelges, specified like:
/// <code>#requires -RunAsAdministrator</code>
/// If nothing is specified, this property is false.
/// </summary>
public bool IsElevationRequired { get ; internal set ; }
}
/// <summary>
/// A ScriptBlockAst is the root ast node for a complete script.
/// </summary>
public class ScriptBlockAst : Ast , IParameterMetadataProvider
{
private static readonly ReadOnlyCollection < AttributeAst > EmptyAttributeList =
Utils . EmptyReadOnlyCollection < AttributeAst >();
private static readonly ReadOnlyCollection < UsingStatementAst > EmptyUsingStatementList =
Utils . EmptyReadOnlyCollection < UsingStatementAst >();
internal bool HadErrors { get ; set ; }
internal bool IsConfiguration { get ; private set ; }
internal bool PostParseChecksPerformed { get ; set ; }
/// <summary>
/// Construct a ScriptBlockAst that uses explicitly named begin/process/end blocks.
/// </summary>
/// <param name="extent">The extent of the script block.</param>
/// <param name="usingStatements">The list of using statments, may be null.</param>
/// <param name="attributes">The set of attributes for the script block.</param>
/// <param name="paramBlock">The ast for the param block, may be null.</param>
/// <param name="beginBlock">The ast for the begin block, may be null.</param>
/// <param name="processBlock">The ast for the process block, may be null.</param>
/// <param name="endBlock">The ast for the end block, may be null.</param>
/// <param name="dynamicParamBlock">The ast for the dynamicparam block, may be null.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")]
public ScriptBlockAst ( IScriptExtent extent ,
IEnumerable < UsingStatementAst > usingStatements ,
IEnumerable < AttributeAst > attributes ,
ParamBlockAst paramBlock ,
NamedBlockAst beginBlock ,
NamedBlockAst processBlock ,
NamedBlockAst endBlock ,
NamedBlockAst dynamicParamBlock )
: base ( extent )
{
SetUsingStatements ( usingStatements );
if ( attributes != null )
{
this . Attributes = new ReadOnlyCollection < AttributeAst >( attributes . ToArray ());
SetParents ( Attributes );
}
else
{
this . Attributes = EmptyAttributeList ;
}
if ( paramBlock != null )
{
this . ParamBlock = paramBlock ;
SetParent ( paramBlock );
}
if ( beginBlock != null )
{
this . BeginBlock = beginBlock ;
SetParent ( beginBlock );
}
if ( processBlock != null )
{
this . ProcessBlock = processBlock ;
SetParent ( processBlock );
}
if ( endBlock != null )
{
this . EndBlock = endBlock ;
SetParent ( endBlock );
}
if ( dynamicParamBlock != null )
{
this . DynamicParamBlock = dynamicParamBlock ;
SetParent ( dynamicParamBlock );
}
}
/// <summary>
/// Construct a ScriptBlockAst that uses explicitly named begin/process/end blocks.
/// </summary>
/// <param name="extent">The extent of the script block.</param>
/// <param name="usingStatements">The list of using statments, may be null.</param>
/// <param name="paramBlock">The ast for the param block, may be null.</param>
/// <param name="beginBlock">The ast for the begin block, may be null.</param>
/// <param name="processBlock">The ast for the process block, may be null.</param>
/// <param name="endBlock">The ast for the end block, may be null.</param>
/// <param name="dynamicParamBlock">The ast for the dynamicparam block, may be null.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")]
public ScriptBlockAst ( IScriptExtent extent ,
IEnumerable < UsingStatementAst > usingStatements ,
ParamBlockAst paramBlock ,
NamedBlockAst beginBlock ,
NamedBlockAst processBlock ,
NamedBlockAst endBlock ,
NamedBlockAst dynamicParamBlock )
: this ( extent , usingStatements , null , paramBlock , beginBlock , processBlock , endBlock , dynamicParamBlock )
{
}
/// <summary>
/// Construct a ScriptBlockAst that uses explicitly named begin/process/end blocks.
/// </summary>
/// <param name="extent">The extent of the script block.</param>
/// <param name="paramBlock">The ast for the param block, may be null.</param>
/// <param name="beginBlock">The ast for the begin block, may be null.</param>
/// <param name="processBlock">The ast for the process block, may be null.</param>
/// <param name="endBlock">The ast for the end block, may be null.</param>
/// <param name="dynamicParamBlock">The ast for the dynamicparam block, may be null.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")]
public ScriptBlockAst ( IScriptExtent extent ,
ParamBlockAst paramBlock ,
NamedBlockAst beginBlock ,
NamedBlockAst processBlock ,
NamedBlockAst endBlock ,
NamedBlockAst dynamicParamBlock )
: this ( extent , null , paramBlock , beginBlock , processBlock , endBlock , dynamicParamBlock )
{
}
/// <summary>
/// Construct a ScriptBlockAst that does not use explicitly named blocks.
/// </summary>
/// <param name="extent">The extent of the script block.</param>
/// <param name="usingStatements">The list of using statments, may be null.</param>
/// <param name="paramBlock">The ast for the param block, may be null.</param>
/// <param name="statements">
/// The statements that go in the end block if <paramref name="isFilter"/> is false, or the
/// process block if <paramref name="isFilter"/> is true.
/// </param>
/// <param name="isFilter">True if the script block is a filter, false if it is a function or workflow.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="statements"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")]
public ScriptBlockAst ( IScriptExtent extent , List < UsingStatementAst > usingStatements , ParamBlockAst paramBlock , StatementBlockAst statements , bool isFilter )
: this ( extent , usingStatements , null , paramBlock , statements , isFilter , false )
{
}
/// <summary>
/// Construct a ScriptBlockAst that does not use explicitly named blocks.
/// </summary>
/// <param name="extent">The extent of the script block.</param>
/// <param name="paramBlock">The ast for the param block, may be null.</param>
/// <param name="statements">
/// The statements that go in the end block if <paramref name="isFilter"/> is false, or the
/// process block if <paramref name="isFilter"/> is true.
/// </param>
/// <param name="isFilter">True if the script block is a filter, false if it is a function or workflow.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="statements"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")]
public ScriptBlockAst ( IScriptExtent extent , ParamBlockAst paramBlock , StatementBlockAst statements , bool isFilter )
: this ( extent , null , null , paramBlock , statements , isFilter , false )
{
}
/// <summary>
/// Construct a ScriptBlockAst that does not use explicitly named blocks.
/// </summary>
/// <param name="extent">The extent of the script block.</param>
/// <param name="paramBlock">The ast for the param block, may be null.</param>
/// <param name="statements">
/// The statements that go in the end block if <paramref name="isFilter"/> is false, or the
/// process block if <paramref name="isFilter"/> is true.
/// </param>
/// <param name="isFilter">True if the script block is a filter, false if it is a function or workflow.</param>
/// <param name="isConfiguration">True if the script block is a configuration</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="statements"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")]
public ScriptBlockAst ( IScriptExtent extent , ParamBlockAst paramBlock , StatementBlockAst statements , bool isFilter , bool isConfiguration )
: this ( extent , null , null , paramBlock , statements , isFilter , isConfiguration )
{
}
/// <summary>
/// Construct a ScriptBlockAst that does not use explicitly named blocks.
/// </summary>
/// <param name="extent">The extent of the script block.</param>
/// <param name="usingStatements">The list of using statments, may be null.</param>
/// <param name="paramBlock">The ast for the param block, may be null.</param>
/// <param name="statements">
/// The statements that go in the end block if <paramref name="isFilter"/> is false, or the
/// process block if <paramref name="isFilter"/> is true.
/// </param>
/// <param name="isFilter">True if the script block is a filter, false if it is a function or workflow.</param>
/// <param name="isConfiguration">True if the script block is a configuration</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="statements"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")]
public ScriptBlockAst ( IScriptExtent extent , IEnumerable < UsingStatementAst > usingStatements , ParamBlockAst paramBlock , StatementBlockAst statements , bool isFilter , bool isConfiguration )
: this ( extent , usingStatements , null , paramBlock , statements , isFilter , isConfiguration )
{
}
/// <summary>
/// Construct a ScriptBlockAst that does not use explicitly named blocks.
/// </summary>
/// <param name="extent">The extent of the script block.</param>
/// <param name="attributes">The attributes for the script block.</param>
/// <param name="paramBlock">The ast for the param block, may be null.</param>
/// <param name="statements">
/// The statements that go in the end block if <paramref name="isFilter"/> is false, or the
/// process block if <paramref name="isFilter"/> is true.
/// </param>
/// <param name="isFilter">True if the script block is a filter, false if it is a function or workflow.</param>
/// <param name="isConfiguration">True if the script block is a configuration</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="statements"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")]
public ScriptBlockAst ( IScriptExtent extent , IEnumerable < AttributeAst > attributes , ParamBlockAst paramBlock , StatementBlockAst statements , bool isFilter , bool isConfiguration )
: this ( extent , null , attributes , paramBlock , statements , isFilter , isConfiguration )
{
}
/// <summary>
/// Construct a ScriptBlockAst that does not use explicitly named blocks.
/// </summary>
/// <param name="extent">The extent of the script block.</param>
/// <param name="usingStatements">The list of using statments, may be null.</param>
/// <param name="attributes">The attributes for the script block.</param>
/// <param name="paramBlock">The ast for the param block, may be null.</param>
/// <param name="statements">
/// The statements that go in the end block if <paramref name="isFilter"/> is false, or the
/// process block if <paramref name="isFilter"/> is true.
/// </param>
/// <param name="isFilter">True if the script block is a filter, false if it is a function or workflow.</param>
/// <param name="isConfiguration">True if the script block is a configuration</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="statements"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")]
public ScriptBlockAst ( IScriptExtent extent , IEnumerable < UsingStatementAst > usingStatements , IEnumerable < AttributeAst > attributes , ParamBlockAst paramBlock , StatementBlockAst statements , bool isFilter , bool isConfiguration )
: base ( extent )
{
SetUsingStatements ( usingStatements );
if ( attributes != null )
{
this . Attributes = new ReadOnlyCollection < AttributeAst >( attributes . ToArray ());
SetParents ( Attributes );
}
else
{
this . Attributes = EmptyAttributeList ;
}
if ( statements == null )
{
throw PSTraceSource . NewArgumentNullException ( "statements" );
}
if ( paramBlock != null )
{
this . ParamBlock = paramBlock ;
SetParent ( paramBlock );
}
if ( isFilter )
{
this . ProcessBlock = new NamedBlockAst ( statements . Extent , TokenKind . Process , statements , true );
SetParent ( ProcessBlock );
}
else
{
this . EndBlock = new NamedBlockAst ( statements . Extent , TokenKind . End , statements , true );
this . IsConfiguration = isConfiguration ;
SetParent ( EndBlock );
}
}
private void SetUsingStatements ( IEnumerable < UsingStatementAst > usingStatements )
{
if ( usingStatements != null )
{
this . UsingStatements = new ReadOnlyCollection < UsingStatementAst >( usingStatements . ToArray ());
SetParents ( UsingStatements );
}
else
{
this . UsingStatements = EmptyUsingStatementList ;
}
}
/// <summary>
/// The asts for attributes (such as [DscLocalConfigurationManager()]) used before the scriptblock.
/// This property is never null.
/// </summary>
public ReadOnlyCollection < AttributeAst > Attributes { get ; private set ; }
/// <summary>
/// The asts for any using statements. This property is never null.
/// Elements of the collection are instances of either <see cref="UsingStatementAst"/>
/// or (only in error cases) <see cref="ErrorStatementAst"/>.
/// </summary>
public ReadOnlyCollection < UsingStatementAst > UsingStatements { get ; private set ; }
/// <summary>
/// The ast representing the parameters for a script block, or null if no param block was specified.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Param")]
public ParamBlockAst ParamBlock { get ; private set ; }
/// <summary>
/// The ast representing the begin block for a script block, or null if no begin block was specified.
/// </summary>
public NamedBlockAst BeginBlock { get ; private set ; }
/// <summary>
/// The ast representing the process block for a script block, or null if no process block was specified.
/// </summary>
public NamedBlockAst ProcessBlock { get ; private set ; }
/// <summary>
/// The ast representing the end block for a script block, or null if no end block was specified.
/// </summary>
public NamedBlockAst EndBlock { get ; private set ; }
/// <summary>
/// The ast representing the dynamicparam block for a script block, or null if no dynamicparam block was specified.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Param")]
public NamedBlockAst DynamicParamBlock { get ; private set ; }
/// <summary>
/// All of the parsed information from any #requires in the script, or null if #requires was not used.
/// This property is only set for the top level script block (where <see cref="Ast.Parent"/>) is null.
/// </summary>
public ScriptRequirements ScriptRequirements { get ; internal set ; }
/// <summary>
/// Return the help content, if any, for the script block.
/// </summary>
public CommentHelpInfo GetHelpContent ()
{
Dictionary < Ast , Token []> scriptBlockTokenCache = new Dictionary < Ast , Token []>();
var commentTokens = HelpCommentsParser . GetHelpCommentTokens ( this , scriptBlockTokenCache );
if ( commentTokens != null )
{
return HelpCommentsParser . GetHelpContents ( commentTokens . Item1 , commentTokens . Item2 );
}
return null ;
}
/// <summary>
/// Convert the ast into a script block that can be invoked.
/// </summary>
/// <returns>The compiled script block.</returns>
/// <exception cref="ParseException">
/// Thrown if there are any semantic errors in the ast.
/// </exception>
public ScriptBlock GetScriptBlock ()
{
if (! PostParseChecksPerformed )
{
Parser parser = new Parser ();
PerformPostParseChecks ( parser );
if ( parser . ErrorList . Any ())
{
throw new ParseException ( parser . ErrorList . ToArray ());
}
}
if ( HadErrors )
{
throw new PSInvalidOperationException ();
}
return new ScriptBlock ( this , isFilter : false );
}
/// <summary>
/// Copy the ScriptBlockAst instance
/// </summary>
public override Ast Copy ()
{
var newParamBlock = CopyElement ( this . ParamBlock );
var newBeginBlock = CopyElement ( this . BeginBlock );
var newProcessBlock = CopyElement ( this . ProcessBlock );
var newEndBlock = CopyElement ( this . EndBlock );
var newDynamicParamBlock = CopyElement ( this . DynamicParamBlock );
var newAttributes = CopyElements ( this . Attributes );
var newUsingStatements = CopyElements ( this . UsingStatements );
var scriptBlockAst = new ScriptBlockAst ( this . Extent , newUsingStatements , newAttributes , newParamBlock , newBeginBlock , newProcessBlock ,
newEndBlock , newDynamicParamBlock )
{
IsConfiguration = this . IsConfiguration ,
ScriptRequirements = this . ScriptRequirements
};
return scriptBlockAst ;
}
internal string ToStringForSerialization ()
{
string result = this . ToString ();
if ( Parent != null )
{
// Parent is FunctionDefinitionAst or ScriptBlockExpressionAst
// The extent includes curlies which we want to exclude.
Diagnostics . Assert ( result [ 0 ] == '{' && result [ result . Length - 1 ] == '}' ,
"There is an incorrect assumption about the extent." );
result = result . Substring ( 1 , result . Length - 2 );
}
return result ;
}
internal string ToStringForSerialization ( Tuple < List < VariableExpressionAst >, string > usingVariablesTuple , int initialStartOffset , int initialEndOffset )
{
Diagnostics . Assert ( usingVariablesTuple . Item1 != null && usingVariablesTuple . Item1 . Count > 0 && ! string . IsNullOrEmpty ( usingVariablesTuple . Item2 ),
"Caller makes sure the value passed in is not null or empty" );
Diagnostics . Assert ( initialStartOffset < initialEndOffset && initialStartOffset >= this . Extent . StartOffset && initialEndOffset <= this . Extent . EndOffset ,
"Caller makes sure the section is within the ScriptBlockAst" );
List < VariableExpressionAst > usingVars = usingVariablesTuple . Item1 ; // A list of using variables
string newParams = usingVariablesTuple . Item2 ; // The new parameters are seperated by the comma
// astElements contains
// -- UsingVariable
// -- ParamBlockAst
var astElements = new List < Ast >( usingVars );
if ( ParamBlock != null )
{
astElements . Add ( ParamBlock );
}
int indexOffset = this . Extent . StartOffset ;
int startOffset = initialStartOffset - indexOffset ;
int endOffset = initialEndOffset - indexOffset ;
string script = this . ToString ();
var newScript = new StringBuilder ();
foreach ( var ast in astElements . OrderBy ( ast => ast . Extent . StartOffset ))
{
int astStartOffset = ast . Extent . StartOffset - indexOffset ;
int astEndOffset = ast . Extent . EndOffset - indexOffset ;
// Skip the ast that is before the section that we care about
if ( astStartOffset < startOffset ) { continue ; }
// We are done processing the section that we care about
if ( astStartOffset >= endOffset ) { break ; }
var varAst = ast as VariableExpressionAst ;
if ( varAst != null )
{
string varName = varAst . VariablePath . UserPath ;
string varSign = varAst . Splatted ? "@" : "$" ;
string newVarName = varSign + UsingExpressionAst . UsingPrefix + varName ;
newScript . Append ( script . Substring ( startOffset , astStartOffset - startOffset ));
newScript . Append ( newVarName );
startOffset = astEndOffset ;
}
else
{
var paramAst = ast as ParamBlockAst ;
Diagnostics . Assert ( paramAst != null , "The elements in astElements are either ParamBlockAst or VariableExpressionAst" );
int currentOffset ;
if ( paramAst . Parameters . Count == 0 )
{
currentOffset = astEndOffset - 1 ;
}
else
{
var firstParam = paramAst . Parameters [ 0 ];
currentOffset = firstParam . Attributes . Count == 0 ? firstParam . Name . Extent . StartOffset - indexOffset : firstParam . Attributes [ 0 ]. Extent . StartOffset - indexOffset ;
newParams += ",\n" ;
}
newScript . Append ( script . Substring ( startOffset , currentOffset - startOffset ));
newScript . Append ( newParams );
startOffset = currentOffset ;
}
}
newScript . Append ( script . Substring ( startOffset , endOffset - startOffset ));
string result = newScript . ToString ();
if ( Parent != null && initialStartOffset == this . Extent . StartOffset && initialEndOffset == this . Extent . EndOffset )
{
// Parent is FunctionDefinitionAst or ScriptBlockExpressionAst
// The extent includes curlies which we want to exclude.
Diagnostics . Assert ( result [ 0 ] == '{' && result [ result . Length - 1 ] == '}' ,
"There is an incorrect assumption about the extent." );
result = result . Substring ( 1 , result . Length - 2 );
}
return result ;
}
internal void PerformPostParseChecks ( Parser parser )
{
bool etwEnabled = ParserEventSource . Log . IsEnabled ();
if ( etwEnabled ) ParserEventSource . Log . ResolveSymbolsStart ();
SymbolResolver . ResolveSymbols ( parser , this );
if ( etwEnabled )
{
ParserEventSource . Log . ResolveSymbolsStop ();
ParserEventSource . Log . SemanticChecksStart ();
}
SemanticChecks . CheckAst ( parser , this );
if ( etwEnabled ) ParserEventSource . Log . SemanticChecksStop ();
Diagnostics . Assert ( PostParseChecksPerformed , "Post parse checks not set during semantic checks" );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
// The following is used when we don't find OutputType, which is checked elsewhere.
if ( BeginBlock != null )
{
foreach ( var typename in BeginBlock . GetInferredType ( context ))
{
yield return typename ;
}
}
if ( ProcessBlock != null )
{
foreach ( var typename in ProcessBlock . GetInferredType ( context ))
{
yield return typename ;
}
}
if ( EndBlock != null )
{
foreach ( var typename in EndBlock . GetInferredType ( context ))
{
yield return typename ;
}
}
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitScriptBlock ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitScriptBlock ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
var visitor2 = visitor as AstVisitor2 ;
if ( visitor2 != null )
{
if ( action == AstVisitAction . Continue )
{
foreach ( var usingStatement in UsingStatements )
{
action = usingStatement . InternalVisit ( visitor2 );
if ( action != AstVisitAction . Continue )
{
break ;
}
}
}
if ( action == AstVisitAction . Continue )
{
foreach ( var attr in Attributes )
{
action = attr . InternalVisit ( visitor2 );
if ( action != AstVisitAction . Continue )
{
break ;
}
}
}
}
if ( action == AstVisitAction . Continue && ParamBlock != null )
action = ParamBlock . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue && DynamicParamBlock != null )
action = DynamicParamBlock . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue && BeginBlock != null )
action = BeginBlock . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue && ProcessBlock != null )
action = ProcessBlock . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue && EndBlock != null )
action = EndBlock . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
#region IParameterMetadataProvider implementation
RuntimeDefinedParameterDictionary IParameterMetadataProvider . GetParameterMetadata ( bool automaticPositions , ref bool usesCmdletBinding )
{
if ( ParamBlock != null )
{
return Compiler . GetParameterMetaData ( ParamBlock . Parameters , automaticPositions , ref usesCmdletBinding );
}
return new RuntimeDefinedParameterDictionary { Data = RuntimeDefinedParameterDictionary . EmptyParameterArray };
}
IEnumerable < Attribute > IParameterMetadataProvider . GetScriptBlockAttributes ()
{
for ( int index = 0 ; index < Attributes . Count ; index ++)
{
var attributeAst = Attributes [ index ];
yield return Compiler . GetAttribute ( attributeAst );
}
if ( ParamBlock != null )
{
for ( int index = 0 ; index < ParamBlock . Attributes . Count ; index ++)
{
var attributeAst = ParamBlock . Attributes [ index ];
yield return Compiler . GetAttribute ( attributeAst );
}
}
}
ReadOnlyCollection < ParameterAst > IParameterMetadataProvider . Parameters
{
get { return ( ParamBlock != null ) ? this . ParamBlock . Parameters : null ; }
}
ScriptBlockAst IParameterMetadataProvider . Body { get { return this ; } }
#region PowerShell Conversion
PowerShell IParameterMetadataProvider . GetPowerShell ( ExecutionContext context , Dictionary < string , object > variables , bool isTrustedInput ,
bool filterNonUsingVariables , bool? createLocalScope , params object [] args )
{
ExecutionContext . CheckStackDepth ();
return ScriptBlockToPowerShellConverter . Convert ( this , null , isTrustedInput , context , variables , filterNonUsingVariables , createLocalScope , args );
}
string IParameterMetadataProvider . GetWithInputHandlingForInvokeCommand ()
{
return GetWithInputHandlingForInvokeCommandImpl ( null );
}
Tuple < string , string > IParameterMetadataProvider . GetWithInputHandlingForInvokeCommandWithUsingExpression (
Tuple < List < VariableExpressionAst >, string > usingVariablesTuple )
{
string additionalNewParams = usingVariablesTuple . Item2 ;
string scriptBlockText = GetWithInputHandlingForInvokeCommandImpl ( usingVariablesTuple );
string paramText = null ;
if ( ParamBlock == null )
{
paramText = "param(" + additionalNewParams + ")" + Environment . NewLine ;
}
return new Tuple < string , string >( paramText , scriptBlockText );
}
string GetWithInputHandlingForInvokeCommandImpl ( Tuple < List < VariableExpressionAst >, string > usingVariablesTuple )
{
// do not add "$input |" to complex pipelines
string unused1 ;
string unused2 ;
var pipelineAst = GetSimplePipeline ( false , out unused1 , out unused2 );
if ( pipelineAst == null )
{
return ( usingVariablesTuple == null )
? this . ToStringForSerialization ()
: this . ToStringForSerialization ( usingVariablesTuple , this . Extent . StartOffset , this . Extent . EndOffset );
}
// do not add "$input |" to pipelines beginning with an expression
if ( pipelineAst . PipelineElements [ 0 ] is CommandExpressionAst )
{
return ( usingVariablesTuple == null )
? this . ToStringForSerialization ()
: this . ToStringForSerialization ( usingVariablesTuple , this . Extent . StartOffset , this . Extent . EndOffset );
}
// do not add "$input |" to commands that reference $input in their arguments
if ( AstSearcher . IsUsingDollarInput ( this ))
{
return ( usingVariablesTuple == null )
? this . ToStringForSerialization ()
: this . ToStringForSerialization ( usingVariablesTuple , this . Extent . StartOffset , this . Extent . EndOffset );
}
// all checks above failed - change script into "$input | <original script>"
var sb = new StringBuilder ();
if ( ParamBlock != null )
{
string paramText = ( usingVariablesTuple == null )
? ParamBlock . ToString ()
: this . ToStringForSerialization ( usingVariablesTuple , ParamBlock . Extent . StartOffset , ParamBlock . Extent . EndOffset );
sb . Append ( paramText );
}
sb . Append ( "$input |" );
string pipelineText = ( usingVariablesTuple == null )
? pipelineAst . ToString ()
: this . ToStringForSerialization ( usingVariablesTuple , pipelineAst . Extent . StartOffset , pipelineAst . Extent . EndOffset );
sb . Append ( pipelineText );
return sb . ToString ();
}
#endregion PowerShell Conversion
bool IParameterMetadataProvider . UsesCmdletBinding ()
{
bool usesCmdletBinding = false ;
if ( ParamBlock != null )
{
usesCmdletBinding = this . ParamBlock . Attributes . Any ( attribute => typeof ( CmdletBindingAttribute ) == attribute . TypeName . GetReflectionAttributeType ());
if (! usesCmdletBinding )
{
usesCmdletBinding = ParamBlockAst . UsesCmdletBinding ( ParamBlock . Parameters );
}
}
return usesCmdletBinding ;
}
#endregion IParameterMetadataProvider implementation
internal PipelineAst GetSimplePipeline ( bool allowMultiplePipelines , out string errorId , out string errorMsg )
{
if ( BeginBlock != null || ProcessBlock != null || DynamicParamBlock != null )
{
errorId = "CanConvertOneClauseOnly" ;
errorMsg = AutomationExceptions . CanConvertOneClauseOnly ;
return null ;
}
if ( EndBlock == null || EndBlock . Statements . Count < 1 )
{
errorId = "CantConvertEmptyPipeline" ;
errorMsg = AutomationExceptions . CantConvertEmptyPipeline ;
return null ;
}
if ( EndBlock . Traps != null && EndBlock . Traps . Any ())
{
errorId = "CantConvertScriptBlockWithTrap" ;
errorMsg = AutomationExceptions . CantConvertScriptBlockWithTrap ;
return null ;
}
// Make sure all statements are pipelines.
if ( EndBlock . Statements . Any ( ast => !( ast is PipelineAst )))
{
errorId = "CanOnlyConvertOnePipeline" ;
errorMsg = AutomationExceptions . CanOnlyConvertOnePipeline ;
return null ;
}
if ( EndBlock . Statements . Count != 1 && ! allowMultiplePipelines )
{
errorId = "CanOnlyConvertOnePipeline" ;
errorMsg = AutomationExceptions . CanOnlyConvertOnePipeline ;
return null ;
}
errorId = null ;
errorMsg = null ;
return EndBlock . Statements [ 0 ] as PipelineAst ;
}
}
/// <summary>
/// The ast representing the param statement in a script block.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Param")]
public class ParamBlockAst : Ast
{
private static readonly ReadOnlyCollection < AttributeAst > EmptyAttributeList =
Utils . EmptyReadOnlyCollection < AttributeAst >();
private static readonly ReadOnlyCollection < ParameterAst > EmptyParameterList =
Utils . EmptyReadOnlyCollection < ParameterAst >();
/// <summary>
/// Construct the ast for a param statement of a script block.
/// </summary>
/// <param name="extent">The extent of the param statement, from any possible attributes to the closing paren.</param>
/// <param name="attributes">The attributes (such as [cmdletbinding()]) specified on the param statement. May be null.</param>
/// <param name="parameters">The parameters to the script block. May be null.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public ParamBlockAst ( IScriptExtent extent , IEnumerable < AttributeAst > attributes , IEnumerable < ParameterAst > parameters )
: base ( extent )
{
if ( attributes != null )
{
this . Attributes = new ReadOnlyCollection < AttributeAst >( attributes . ToArray ());
SetParents ( Attributes );
}
else
{
this . Attributes = EmptyAttributeList ;
}
if ( parameters != null )
{
this . Parameters = new ReadOnlyCollection < ParameterAst >( parameters . ToArray ());
SetParents ( Parameters );
}
else
{
this . Parameters = EmptyParameterList ;
}
}
/// <summary>
/// The asts for attributes (such as [cmdletbinding()]) used before the param keyword.
/// </summary>
public ReadOnlyCollection < AttributeAst > Attributes { get ; private set ; }
/// <summary>
/// The asts for the parameters of the param statement.
/// </summary>
public ReadOnlyCollection < ParameterAst > Parameters { get ; private set ; }
/// <summary>
/// Copy the ParamBlockAst instance
/// </summary>
public override Ast Copy ()
{
var newAttributes = CopyElements ( this . Attributes );
var newParameters = CopyElements ( this . Parameters );
return new ParamBlockAst ( this . Extent , newAttributes , newParameters );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitParamBlock ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitParamBlock ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Attributes . Count ; index ++)
{
var attributeAst = Attributes [ index ];
action = attributeAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Parameters . Count ; index ++)
{
var paramAst = Parameters [ index ];
action = paramAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
internal static bool UsesCmdletBinding ( IEnumerable < ParameterAst > parameters )
{
bool usesCmdletBinding = false ;
foreach ( var parameter in parameters )
{
usesCmdletBinding = parameter . Attributes . Any ( attribute => ( attribute . TypeName . GetReflectionAttributeType () != null ) &&
attribute . TypeName . GetReflectionAttributeType () == typeof ( ParameterAttribute ));
if ( usesCmdletBinding )
{
break ;
}
}
return usesCmdletBinding ;
}
}
/// <summary>
/// The ast representing a begin, process, end, or dynamicparam block in a scriptblock. This ast is used even
/// when the block is unnamed, in which case the block is either an end block (for functions) or process block (for filters).
/// </summary>
public class NamedBlockAst : Ast
{
/// <summary>
/// Construct the ast for a begin, process, end, or dynamic param block.
/// </summary>
/// <param name="extent">
/// The extent of the block. If <paramref name="unnamed"/> is false, the extent includes
/// the keyword through the closing curly, otherwise the extent is the as the extent of <paramref name="statementBlock"/>.
/// </param>
/// <param name="blockName">
/// The kind of block, must be one of:
/// <list type="bullet">
/// <item><see cref="TokenKind.Begin"/></item>
/// <item><see cref="TokenKind.Process"/></item>
/// <item><see cref="TokenKind.End"/></item>
/// <item><see cref="TokenKind.Dynamicparam"/></item>
/// </list>
/// </param>
/// <param name="statementBlock">The ast for the statements in this named block.</param>
/// <param name="unnamed">True if the block was not explicitly named.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="statementBlock"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="blockName"/> is not one of the valid kinds for a named block,
/// or if <paramref name="unnamed"/> is <c>true</c> and <paramref name="blockName"/> is neither
/// <see cref="TokenKind.Process"/> nor <see cref="TokenKind.End"/>.
/// </exception>
public NamedBlockAst ( IScriptExtent extent , TokenKind blockName , StatementBlockAst statementBlock , bool unnamed )
: base ( extent )
{
// Validate the block name. If the block is unnamed, it must be an End block (for a function)
// or Process block (for a filter).
if (! blockName . HasTrait ( TokenFlags . ScriptBlockBlockName )
|| ( unnamed && ( blockName == TokenKind . Begin || blockName == TokenKind . Dynamicparam )))
{
throw PSTraceSource . NewArgumentException ( "blockName" );
}
if ( statementBlock == null )
{
throw PSTraceSource . NewArgumentNullException ( "statementBlock" );
}
this . Unnamed = unnamed ;
this . BlockKind = blockName ;
var statements = statementBlock . Statements ;
this . Statements = statements ;
for ( int index = 0 ; index < statements . Count ; index ++)
{
var stmt = statements [ index ];
stmt . ClearParent ();
}
SetParents ( statements );
var traps = statementBlock . Traps ;
if ( traps != null && traps . Any ())
{
this . Traps = traps ;
for ( int index = 0 ; index < traps . Count ; index ++)
{
var trap = traps [ index ];
trap . ClearParent ();
}
SetParents ( traps );
}
if (! unnamed )
{
var statementsExtent = statementBlock . Extent as InternalScriptExtent ;
if ( statementsExtent != null )
{
this . OpenCurlyExtent = new InternalScriptExtent ( statementsExtent . PositionHelper , statementsExtent . StartOffset , statementsExtent . StartOffset + 1 );
this . CloseCurlyExtent = new InternalScriptExtent ( statementsExtent . PositionHelper , statementsExtent . EndOffset - 1 , statementsExtent . EndOffset );
}
}
}
/// <summary>
/// For a function/filter that did not explicitly name the end/process block (which is quite common),
/// this property will return true.
/// </summary>
public bool Unnamed { get ; private set ; }
/// <summary>
/// The kind of block, always one of:
/// <list type="bullet">
/// <item><see cref="TokenKind.Begin"/></item>
/// <item><see cref="TokenKind.Process"/></item>
/// <item><see cref="TokenKind.End"/></item>
/// <item><see cref="TokenKind.Dynamicparam"/></item>
/// </list>
/// </summary>
public TokenKind BlockKind { get ; private set ; }
/// <summary>
/// The asts for all of the statements represented by this statement block. This property is never null.
/// </summary>
public ReadOnlyCollection < StatementAst > Statements { get ; private set ; }
/// <summary>
/// The asts for all of the trap statements specified by this statement block, or null if no trap statements were
/// specified in this block.
/// </summary>
public ReadOnlyCollection < TrapStatementAst > Traps { get ; private set ; }
/// <summary>
/// Copy the NamedBlockAst instance
/// </summary>
public override Ast Copy ()
{
var newTraps = CopyElements ( this . Traps );
var newStatements = CopyElements ( this . Statements );
var statementBlockExtent = this . Extent ;
if ( this . OpenCurlyExtent != null && this . CloseCurlyExtent != null )
{
// For explicitly named block, the Extent of the StatementBlockAst is not
// the same as the Extent of the NamedBlockAst. In this case, reconstruct
// the Extent of the StatementBlockAst from openExtent/closeExtent.
var openExtent = ( InternalScriptExtent ) this . OpenCurlyExtent ;
var closeExtent = ( InternalScriptExtent ) this . CloseCurlyExtent ;
statementBlockExtent = new InternalScriptExtent ( openExtent . PositionHelper , openExtent . StartOffset , closeExtent . EndOffset );
}
var statementBlock = new StatementBlockAst ( statementBlockExtent , newStatements , newTraps );
return new NamedBlockAst ( this . Extent , this . BlockKind , statementBlock , this . Unnamed );
}
// Used by the debugger for command breakpoints
internal IScriptExtent OpenCurlyExtent { get ; private set ; }
internal IScriptExtent CloseCurlyExtent { get ; private set ; }
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Statements . SelectMany ( ast => ast . GetInferredType ( context ));
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitNamedBlock ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitNamedBlock ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = StatementBlockAst . InternalVisit ( visitor , Traps , Statements , action );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast representing a named attribute argument. For example, in <c>[Parameter(Mandatory=$true)]</c>, this ast
/// represents <c>Mandatory=$true</c>.
/// </summary>
public class NamedAttributeArgumentAst : Ast
{
/// <summary>
/// Construct the ast for a named attribute argument.
/// </summary>
/// <param name="extent">
/// The extent of the named attribute argument, starting with the name, ending with the expression, or if the expression
/// is omitted from the source, then ending at the end of the name.
/// </param>
/// <param name="argumentName">The name of the argument specified. May not be null or empty.</param>
/// <param name="argument">The argument expression. May not be null even if the expression is omitted from the source.</param>
/// <param name="expressionOmitted">
/// True when an explicit argument is not provided in the source, e.g. <c>[Parameter(Mandatory)]</c>. In this case,
/// an ast for the argument expression must still be provided.
/// </param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="argumentName"/>, or <paramref name="argument"/> is null, or if
/// <paramref name="argumentName"/> is an empty string.
/// </exception>
public NamedAttributeArgumentAst ( IScriptExtent extent , string argumentName , ExpressionAst argument , bool expressionOmitted )
: base ( extent )
{
if ( string . IsNullOrEmpty ( argumentName ))
{
throw PSTraceSource . NewArgumentNullException ( "argumentName" );
}
if ( argument == null )
{
throw PSTraceSource . NewArgumentNullException ( "argument" );
}
this . Argument = argument ;
SetParent ( argument );
this . ArgumentName = argumentName ;
this . ExpressionOmitted = expressionOmitted ;
}
/// <summary>
/// The named argument specified by this ast, is never null or empty.
/// </summary>
public string ArgumentName { get ; private set ; }
/// <summary>
/// The ast of the value of the argument specified by this ast. This property is never null.
/// </summary>
public ExpressionAst Argument { get ; private set ; }
/// <summary>
/// If the source omitted an expression, this returns true, otherwise false. This allows a caller to distinguish
/// the difference between <c>[Parameter(Mandatory)]</c> and <c>[Parameter(Mandatory=$true)]</c>
/// </summary>
public bool ExpressionOmitted { get ; private set ; }
/// <summary>
/// Copy the NamedAttributeArgumentAst instance
/// </summary>
public override Ast Copy ()
{
var newArgument = CopyElement ( this . Argument );
return new NamedAttributeArgumentAst ( this . Extent , this . ArgumentName , newArgument , this . ExpressionOmitted );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitNamedAttributeArgument ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitNamedAttributeArgument ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Argument . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// An abstract base class representing attributes that accept optional arguments, e.g. <c>[Parameter()]</c>, as well as
/// type constraints, such as <c>[int]</c>.
/// </summary>
public abstract class AttributeBaseAst : Ast
{
/// <summary>
/// Initiale the common fields for an attribute.
/// </summary>
/// <param name="extent">The extent of the attribute, from the opening '[' to the closing ']'.</param>
/// <param name="typeName">The type named by the attribute.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="typeName"/> is null.
/// </exception>
protected AttributeBaseAst ( IScriptExtent extent , ITypeName typeName )
: base ( extent )
{
if ( typeName == null )
{
throw PSTraceSource . NewArgumentNullException ( "typeName" );
}
this . TypeName = typeName ;
}
/// <summary>
/// The type name for the attribute. This property is never null.
/// </summary>
public ITypeName TypeName { get ; private set ; }
internal abstract Attribute GetAttribute ();
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
}
/// <summary>
/// The ast representing an attribute with optional positional and named arguments.
/// </summary>
public class AttributeAst : AttributeBaseAst
{
private static readonly ReadOnlyCollection < ExpressionAst > EmptyPositionalArguments =
Utils . EmptyReadOnlyCollection < ExpressionAst >();
private static readonly ReadOnlyCollection < NamedAttributeArgumentAst > EmptyNamedAttributeArguments =
Utils . EmptyReadOnlyCollection < NamedAttributeArgumentAst >();
/// <summary>
/// Construct an attribute ast.
/// </summary>
/// <param name="extent">The extent of the attribute from opening '[' to closing ']'.</param>
/// <param name="namedArguments">The named arguments, may be null.</param>
/// <param name="positionalArguments">The positional arguments, may be null.</param>
/// <param name="typeName">The attribute name.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="typeName"/> is null.
/// </exception>
public AttributeAst ( IScriptExtent extent ,
ITypeName typeName ,
IEnumerable < ExpressionAst > positionalArguments ,
IEnumerable < NamedAttributeArgumentAst > namedArguments )
: base ( extent , typeName )
{
if ( positionalArguments != null )
{
this . PositionalArguments = new ReadOnlyCollection < ExpressionAst >( positionalArguments . ToArray ());
SetParents ( PositionalArguments );
}
else
{
this . PositionalArguments = EmptyPositionalArguments ;
}
if ( namedArguments != null )
{
this . NamedArguments = new ReadOnlyCollection < NamedAttributeArgumentAst >( namedArguments . ToArray ());
SetParents ( NamedArguments );
}
else
{
this . NamedArguments = EmptyNamedAttributeArguments ;
}
}
/// <summary>
/// The asts for the attribute arguments specified positionally.
/// </summary>
public ReadOnlyCollection < ExpressionAst > PositionalArguments { get ; private set ; }
/// <summary>
/// The asts for the named attribute arguments.
/// </summary>
public ReadOnlyCollection < NamedAttributeArgumentAst > NamedArguments { get ; private set ; }
/// <summary>
/// Copy the AttributeAst instance
/// </summary>
public override Ast Copy ()
{
var newPositionalArguments = CopyElements ( this . PositionalArguments );
var newNamedArguments = CopyElements ( this . NamedArguments );
return new AttributeAst ( this . Extent , this . TypeName , newPositionalArguments , newNamedArguments );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitAttribute ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitAttribute ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < PositionalArguments . Count ; index ++)
{
var expressionAst = PositionalArguments [ index ];
action = expressionAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < NamedArguments . Count ; index ++)
{
var namedAttributeArgumentAst = NamedArguments [ index ];
action = namedAttributeArgumentAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
internal override Attribute GetAttribute ()
{
return Compiler . GetAttribute ( this );
}
}
/// <summary>
/// The ast representing a type constraint, which is simply a typename with no arguments.
/// </summary>
public class TypeConstraintAst : AttributeBaseAst
{
/// <summary>
/// Construct a type constraint from a possibly not yet resolved typename.
/// </summary>
/// <param name="extent">The extent of the constraint, from the opening '[' to the closing ']'.</param>
/// <param name="typeName">The type for the constraint.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="typeName"/> is null.
/// </exception>
public TypeConstraintAst ( IScriptExtent extent , ITypeName typeName )
: base ( extent , typeName )
{
}
/// <summary>
/// Construct a type constraint from a <see cref="Type"/>.
/// </summary>
/// <param name="extent">The extent of the constraint, from the opening '[' to the closing ']'.</param>
/// <param name="type">The type for the constraint.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="type"/> is null.
/// </exception>
public TypeConstraintAst ( IScriptExtent extent , Type type )
: base ( extent , new ReflectionTypeName ( type ))
{
}
/// <summary>
/// Copy the TypeConstraintAst instance
/// </summary>
public override Ast Copy ()
{
return new TypeConstraintAst ( this . Extent , this . TypeName );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitTypeConstraint ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitTypeConstraint ( this );
return visitor . CheckForPostAction ( this , action == AstVisitAction . SkipChildren ? AstVisitAction . Continue : action );
}
#endregion Visitors
internal override Attribute GetAttribute ()
{
return Compiler . GetAttribute ( this );
}
}
/// <summary>
/// The ast representing a parameter to a script. Parameters may appear in one of 2 places, either just after the
/// name of the function, e.g. <c>function foo($a){}</c> or in a param statement, e.g. <c>param($a)</c>.
/// </summary>
public class ParameterAst : Ast
{
private static readonly ReadOnlyCollection < AttributeBaseAst > EmptyAttributeList =
Utils . EmptyReadOnlyCollection < AttributeBaseAst >();
/// <summary>
/// Construct a parameter ast from the name, attributes, and default value.
/// </summary>
/// <param name="extent">The extent of the parameter, including the attributes and default if specified.</param>
/// <param name="name">The name of the variable.</param>
/// <param name="attributes">The attributes, or null if no attributes were specified.</param>
/// <param name="defaultValue">The default value of the parameter, or null if no default value was specified.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="name"/> is null.
/// </exception>
public ParameterAst ( IScriptExtent extent ,
VariableExpressionAst name ,
IEnumerable < AttributeBaseAst > attributes ,
ExpressionAst defaultValue )
: base ( extent )
{
if ( name == null )
{
throw PSTraceSource . NewArgumentNullException ( "name" );
}
if ( attributes != null )
{
this . Attributes = new ReadOnlyCollection < AttributeBaseAst >( attributes . ToArray ());
SetParents ( Attributes );
}
else
{
this . Attributes = EmptyAttributeList ;
}
this . Name = name ;
SetParent ( name );
if ( defaultValue != null )
{
this . DefaultValue = defaultValue ;
SetParent ( defaultValue );
}
}
/// <summary>
/// The asts for any attributes or type constraints specified on the parameter.
/// </summary>
public ReadOnlyCollection < AttributeBaseAst > Attributes { get ; private set ; }
/// <summary>
/// The variable path for the parameter. This property is never null.
/// </summary>
public VariableExpressionAst Name { get ; private set ; }
/// <summary>
/// The ast for the default value of the parameter, or null if no default value was specified.
/// </summary>
public ExpressionAst DefaultValue { get ; private set ; }
/// <summary>
/// Returns the type of the parameter. If the parameter is constrained to be a specific type, that type is returned,
/// otherwise <c>typeof(object)</c> is returned.
/// </summary>
public Type StaticType
{
get
{
Type type = null ;
var typeConstraint = Attributes . OfType < TypeConstraintAst >(). FirstOrDefault ();
if ( typeConstraint != null )
{
type = typeConstraint . TypeName . GetReflectionType ();
}
return type ?? typeof ( object );
}
}
/// <summary>
/// Copy the ParameterAst instance
/// </summary>
public override Ast Copy ()
{
var newName = CopyElement ( this . Name );
var newAttributes = CopyElements ( this . Attributes );
var newDefaultValue = CopyElement ( this . DefaultValue );
return new ParameterAst ( this . Extent , newName , newAttributes , newDefaultValue );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
var typeConstraint = Attributes . OfType < TypeConstraintAst >(). FirstOrDefault ();
if ( typeConstraint != null )
{
yield return new PSTypeName ( typeConstraint . TypeName );
}
foreach ( var attributeAst in Attributes . OfType < AttributeAst >())
{
PSTypeNameAttribute attribute = null ;
try
{
attribute = attributeAst . GetAttribute () as PSTypeNameAttribute ;
}
catch ( RuntimeException ) { }
if ( attribute != null )
{
yield return new PSTypeName ( attribute . PSTypeName );
}
}
}
internal string GetTooltip ()
{
var typeConstraint = Attributes . OfType < TypeConstraintAst >(). FirstOrDefault ();
var type = typeConstraint != null ? typeConstraint . TypeName . FullName : "object" ;
return type + " " + Name . VariablePath . UserPath ;
}
/// <summary>
/// Get the text that represents this ParameterAst based on the $using variables passed in.
/// A parameter name cannot be a using variable, but its default value could contain any number of UsingExpressions, for example:
/// function bar ($x = (Get-X @using:defaultSettings.Parameters)) { ... }
/// This method goes through the ParameterAst text and replace each $using variable with its new synthetic name (remove the $using prefix).
/// This method is used when we call Invoke-Command targetting a PSv2 remote machine. In that case, we might need to call this method
/// to process the script block text, since $using prefix cannot be recognized by PSv2.
/// </summary>
/// <param name="orderedUsingVar">A sorted enumerator of using variable asts, ascendingly sorted based on StartOffSet</param>
/// <returns>
/// The text of the ParameterAst with $using variable being replaced with a new variable name.
/// </returns>
internal string GetParamTextWithDollarUsingHandling ( IEnumerator < VariableExpressionAst > orderedUsingVar )
{
int indexOffset = Extent . StartOffset ;
int startOffset = 0 ;
int endOffset = Extent . EndOffset - Extent . StartOffset ;
string paramText = ToString ();
if ( orderedUsingVar . Current == null && ! orderedUsingVar . MoveNext ())
{
return paramText ;
}
var newParamText = new StringBuilder ();
do
{
var varAst = orderedUsingVar . Current ;
int astStartOffset = varAst . Extent . StartOffset - indexOffset ;
int astEndOffset = varAst . Extent . EndOffset - indexOffset ;
// Skip the VariableAst that is before section we care about
if ( astStartOffset < startOffset ) { continue ; }
// We are done processing the current ParameterAst
if ( astStartOffset >= endOffset ) { break ; }
string varName = varAst . VariablePath . UserPath ;
string varSign = varAst . Splatted ? "@" : "$" ;
string newVarName = varSign + UsingExpressionAst . UsingPrefix + varName ;
newParamText . Append ( paramText . Substring ( startOffset , astStartOffset - startOffset ));
newParamText . Append ( newVarName );
startOffset = astEndOffset ;
} while ( orderedUsingVar . MoveNext ());
if ( startOffset == 0 )
{
// Nothing changed within the ParameterAst text
return paramText ;
}
newParamText . Append ( paramText . Substring ( startOffset , endOffset - startOffset ));
return newParamText . ToString ();
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitParameter ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitParameter ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Attributes . Count ; index ++)
{
var attributeAst = Attributes [ index ];
action = attributeAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue )
{
action = Name . InternalVisit ( visitor );
}
if ( action == AstVisitAction . Continue && DefaultValue != null )
{
action = DefaultValue . InternalVisit ( visitor );
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
#endregion Script Blocks
#region Statements
/// <summary>
/// The ast representing a block of statements. The block of statements could be part of a script block or some other
/// statement such as an if statement or while statement.
/// </summary>
public class StatementBlockAst : Ast
{
private static ReadOnlyCollection < StatementAst > emptyStatementCollection = Utils . EmptyReadOnlyCollection < StatementAst >();
/// <summary>
/// Construct a statement block.
/// </summary>
/// <param name="extent">The extent of the statement block. If curly braces are part of the statement block (and
/// not some other ast like in a script block), then the curly braces are included in the extent, otherwise the
/// extent runs from the first statement or trap to the last statement or trap.</param>
/// <param name="statements">The (possibly null) collection of statements.</param>
/// <param name="traps">The (possibly null) collection of trap statements.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public StatementBlockAst ( IScriptExtent extent , IEnumerable < StatementAst > statements , IEnumerable < TrapStatementAst > traps )
: base ( extent )
{
if ( statements != null )
{
this . Statements = new ReadOnlyCollection < StatementAst >( statements . ToArray ());
SetParents ( Statements );
}
else
{
this . Statements = emptyStatementCollection ;
}
if ( traps != null && traps . Any ())
{
this . Traps = new ReadOnlyCollection < TrapStatementAst >( traps . ToArray ());
SetParents ( Traps );
}
}
/// <summary>
/// The asts for all of the statements represented by this statement block. This property is never null.
/// </summary>
public ReadOnlyCollection < StatementAst > Statements { get ; private set ; }
/// <summary>
/// The asts for all of the trap statements specified by this statement block, or null if no trap statements were
/// specified in this block.
/// </summary>
public ReadOnlyCollection < TrapStatementAst > Traps { get ; private set ; }
/// <summary>
/// Copy the StatementBlockAst instance
/// </summary>
public override Ast Copy ()
{
var newStatements = CopyElements ( this . Statements );
var newTraps = CopyElements ( this . Traps );
return new StatementBlockAst ( this . Extent , newStatements , newTraps );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Statements . SelectMany ( nestedAst => nestedAst . GetInferredType ( context ));
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitStatementBlock ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitStatementBlock ( this );
return visitor . CheckForPostAction ( this , InternalVisit ( visitor , Traps , Statements , action ));
}
internal static AstVisitAction InternalVisit ( AstVisitor visitor ,
ReadOnlyCollection < TrapStatementAst > traps ,
ReadOnlyCollection < StatementAst > statements ,
AstVisitAction action )
{
if ( action == AstVisitAction . SkipChildren )
return AstVisitAction . Continue ;
if ( action == AstVisitAction . Continue && traps != null )
{
for ( int index = 0 ; index < traps . Count ; index ++)
{
var trapAst = traps [ index ];
action = trapAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue && statements != null )
{
for ( int index = 0 ; index < statements . Count ; index ++)
{
var statementAst = statements [ index ];
action = statementAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return action ;
}
#endregion Visitors
}
/// <summary>
/// An abstract base class for any statement like an if statement or while statement.
/// </summary>
public abstract class StatementAst : Ast
{
/// <summary>
/// Initialize the common fields of a statement.
/// </summary>
/// <param name="extent">The extent of the statement.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
protected StatementAst ( IScriptExtent extent )
: base ( extent )
{
}
}
/// <summary>
/// Specifies type attributes.
/// </summary>
[Flags]
public enum TypeAttributes
{
/// <summary>No attributes specified.</summary>
None = 0x00 ,
/// <summary>The type specifies a class.</summary>
Class = 0x01 ,
/// <summary>The type specifies an interface.</summary>
Interface = 0x02 ,
/// <summary>The type specifies an enum.</summary>
Enum = 0x04 ,
}
/// <summary>
/// The ast representing a type definition including attributes, base class and
/// implemented interfaces, plus it's members.
/// </summary>
public class TypeDefinitionAst : StatementAst
{
private static readonly ReadOnlyCollection < AttributeAst > EmptyAttributeList =
Utils . EmptyReadOnlyCollection < AttributeAst >();
private static readonly ReadOnlyCollection < MemberAst > EmptyMembersCollection =
Utils . EmptyReadOnlyCollection < MemberAst >();
private static readonly ReadOnlyCollection < TypeConstraintAst > EmptyBaseTypesCollection =
Utils . EmptyReadOnlyCollection < TypeConstraintAst >();
/// <summary>
/// Construct a type definition.
/// </summary>
/// <param name="extent">The extent of the type definition, from any attributes to the closing curly brace.</param>
/// <param name="name">The name of the type</param>
/// <param name="attributes">The attributes, or null if no attributes were specified.</param>
/// <param name="members">The members, or null if no members were specified.</param>
/// <param name="typeAttributes">The attributes (like class or interface) of the type.</param>
/// <param name="baseTypes">Base class and implemented interfaces for the type.</param>
public TypeDefinitionAst ( IScriptExtent extent , string name , IEnumerable < AttributeAst > attributes , IEnumerable < MemberAst > members , TypeAttributes typeAttributes , IEnumerable < TypeConstraintAst > baseTypes )
: base ( extent )
{
if ( string . IsNullOrWhiteSpace ( name ))
{
throw PSTraceSource . NewArgumentNullException ( "name" );
}
if ( attributes != null && attributes . Any ())
{
Attributes = new ReadOnlyCollection < AttributeAst >( attributes . ToArray ());
SetParents ( Attributes );
}
else
{
Attributes = EmptyAttributeList ;
}
if ( members != null && members . Any ())
{
Members = new ReadOnlyCollection < MemberAst >( members . ToArray ());
SetParents ( Members );
}
else
{
Members = EmptyMembersCollection ;
}
if ( baseTypes != null && baseTypes . Any ())
{
BaseTypes = new ReadOnlyCollection < TypeConstraintAst >( baseTypes . ToArray ());
SetParents ( BaseTypes );
}
else
{
BaseTypes = EmptyBaseTypesCollection ;
}
this . Name = name ;
this . TypeAttributes = typeAttributes ;
}
/// <summary>
/// The name of the type.
/// </summary>
public string Name { get ; private set ; }
/// <summary>
/// The asts for the custom attributes specified on the type. This property is never null.
/// </summary>
public ReadOnlyCollection < AttributeAst > Attributes { get ; private set ; }
/// <summary>
/// The asts for the base types. This property is never null.
/// </summary>
public ReadOnlyCollection < TypeConstraintAst > BaseTypes { get ; private set ; }
/// <summary>
/// The asts for the members of the type. This property is never null.
/// </summary>
public ReadOnlyCollection < MemberAst > Members { get ; private set ; }
/// <summary>
/// The type attributes (like class or interface) of the type.
/// </summary>
public TypeAttributes TypeAttributes { get ; private set ; }
/// <summary>
/// Returns true if the type defines an enum.
/// </summary>
public bool IsEnum { get { return ( TypeAttributes & TypeAttributes . Enum ) == TypeAttributes . Enum ; } }
/// <summary>
/// Returns true if the type defines a class.
/// </summary>
public bool IsClass { get { return ( TypeAttributes & TypeAttributes . Class ) == TypeAttributes . Class ; } }
/// <summary>
/// Returns true if the type defines an interface.
/// </summary>
public bool IsInterface { get { return ( TypeAttributes & TypeAttributes . Interface ) == TypeAttributes . Interface ; } }
internal Type Type
{
get
{
return _type ;
}
set
{
// The assert may seem a little bit confusing.
// It's because RuntimeType is not a public class and I don't want to use Name string in assert.
// The basic idea is that Type field should go thru 3 stages:
// 1. null
// 2. TypeBuilder
// 3. RuntimeType
// We also allow wipe type (assign to null), because there could be errors.
Diagnostics . Assert ( value == null || _type == null || _type . GetTypeInfo () is TypeBuilder , "Type must be assigned only once to RuntimeType" );
_type = value ;
}
}
/// <summary>
/// Copy the TypeDefinitionAst
/// </summary>
public override Ast Copy ()
{
return new TypeDefinitionAst ( Extent , Name , CopyElements ( Attributes ), CopyElements ( Members ), TypeAttributes , CopyElements ( BaseTypes ));
}
private Type _type ;
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
var visitor2 = visitor as ICustomAstVisitor2 ;
return visitor2 != null ? visitor2 . VisitTypeDefinition ( this ) : null ;
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = AstVisitAction . Continue ;
var visitor2 = visitor as AstVisitor2 ;
if ( visitor2 != null )
{
action = visitor2 . VisitTypeDefinition ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
}
// REVIEW: should old visitors completely skip the attributes and
// bodies of methods, or should they get a chance to see them. If
// we want to skip them, the code below needs to move up into the
// above test 'vistitor2 != null'.
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Attributes . Count ; index ++)
{
var attribute = Attributes [ index ];
action = attribute . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue )
{
break ;
}
}
}
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < BaseTypes . Count ; index ++)
{
var baseTypes = BaseTypes [ index ];
action = baseTypes . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue )
{
break ;
}
}
}
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Members . Count ; index ++)
{
var member = Members [ index ];
action = member . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue )
{
break ;
}
}
}
return visitor . CheckForPostAction ( this , action );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
#endregion Visitors
}
/// <summary>
/// The kind of using statement.
/// </summary>
public enum UsingStatementKind
{
/// <summary>
/// A parse time reference to an assembly.
/// </summary>
Assembly = 0 ,
/// <summary>
/// A parse time command alias.
/// </summary>
Command = 1 ,
/// <summary>
/// A parse time reference or alias to a module.
/// </summary>
Module = 2 ,
/// <summary>
/// A parse time statement that allows specifying types without their full namespace.
/// </summary>
Namespace = 3 ,
/// <summary>
/// A parse time type alias (type accelerator).
/// </summary>
Type = 4 ,
}
/// <summary>
/// The ast representing a using statement.
/// </summary>
public class UsingStatementAst : StatementAst
{
/// <summary>
/// Construct a simple using statement (one that is not a form of an alias).
/// </summary>
/// <param name="extent">The extent of the using statement including the using keyword.</param>
/// <param name="kind">
/// The kind of using statement, cannot be <see cref="System.Management.Automation.Language.UsingStatementKind.Command"/>
/// or <see cref="System.Management.Automation.Language.UsingStatementKind.Type"/>
/// </param>
/// <param name="name">The item (assembly, module, or namespace) being used.</param>
public UsingStatementAst ( IScriptExtent extent , UsingStatementKind kind , StringConstantExpressionAst name )
: base ( extent )
{
if ( name == null )
{
throw PSTraceSource . NewArgumentNullException ( "name" );
}
if ( kind == UsingStatementKind . Command || kind == UsingStatementKind . Type )
{
throw PSTraceSource . NewArgumentException ( "kind" );
}
UsingStatementKind = kind ;
Name = name ;
SetParent ( Name );
}
/// <summary>
/// Construct a simple (one that is not a form of an alias) using module statement with module specification as hashtable.
/// </summary>
/// <param name="extent">The extent of the using statement including the using keyword.</param>
/// <param name="moduleSpecification">HashtableAst that describes <see cref="Microsoft.PowerShell.Commands.ModuleSpecification"/> object</param>
public UsingStatementAst ( IScriptExtent extent , HashtableAst moduleSpecification )
: base ( extent )
{
if ( moduleSpecification == null )
{
throw PSTraceSource . NewArgumentNullException ( "moduleSpecification" );
}
UsingStatementKind = UsingStatementKind . Module ;
ModuleSpecification = moduleSpecification ;
SetParent ( moduleSpecification );
}
/// <summary>
/// Construct a using statement that aliases an item.
/// </summary>
/// <param name="extent">The extent of the using statement including the using keyword.</param>
/// <param name="kind">
/// The kind of using statement, cannot be <see cref="System.Management.Automation.Language.UsingStatementKind.Assembly"/>.
/// </param>
/// <param name="aliasName">The name of the alias.</param>
/// <param name="resolvedAliasAst">The item being aliased.</param>
public UsingStatementAst ( IScriptExtent extent , UsingStatementKind kind , StringConstantExpressionAst aliasName ,
StringConstantExpressionAst resolvedAliasAst )
: base ( extent )
{
if ( aliasName == null )
{
throw PSTraceSource . NewArgumentNullException ( "aliasName" );
}
if ( resolvedAliasAst == null )
{
throw PSTraceSource . NewArgumentNullException ( "resolvedAliasAst" );
}
if ( kind == UsingStatementKind . Assembly )
{
throw PSTraceSource . NewArgumentException ( "kind" );
}
UsingStatementKind = kind ;
Name = aliasName ;
Alias = resolvedAliasAst ;
SetParent ( Name );
SetParent ( Alias );
}
/// <summary>
/// Construct a using module statement that aliases an item with module specification as hashtable.
/// </summary>
/// <param name="extent">The extent of the using statement including the using keyword.</param>
/// <param name="aliasName">The name of the alias.</param>
/// <param name="moduleSpecification">The module being aliased. Hashtable that describes <see cref="Microsoft.PowerShell.Commands.ModuleSpecification"/></param>
public UsingStatementAst ( IScriptExtent extent , StringConstantExpressionAst aliasName , HashtableAst moduleSpecification )
: base ( extent )
{
if ( moduleSpecification == null )
{
throw PSTraceSource . NewArgumentNullException ( "moduleSpecification" );
}
UsingStatementKind = UsingStatementKind . Module ;
ModuleSpecification = moduleSpecification ;
Name = aliasName ;
SetParent ( moduleSpecification );
}
/// <summary>
/// The kind of using statement.
/// </summary>
public UsingStatementKind UsingStatementKind { get ; private set ; }
/// <summary>
/// When <see cref="Alias"/> is null or <see cref="ModuleSpecification"/> is null, the item being used, otherwise the alias name.
/// </summary>
public StringConstantExpressionAst Name { get ; private set ; }
/// <summary>
/// The name of the item being aliased.
/// This property is mutually exclusive with <see cref="ModuleSpecification"/> property.
/// </summary>
public StringConstantExpressionAst Alias { get ; private set ; }
/// <summary>
/// Hashtable that can be converted to <see cref="Microsoft.PowerShell.Commands.ModuleSpecification"/>. Only for 'using module' case, otherwise null.
/// This property is mutually exclusive with <see cref="Alias"/> property.
/// </summary>
public HashtableAst ModuleSpecification { get ; private set ; }
/// <summary>
/// ModuleInfo about used module. Only for 'using module' case, otherwise null.
/// </summary>
internal PSModuleInfo ModuleInfo { get ; private set ; }
/// <summary>
/// Copy the UsingStatementAst
/// </summary>
public override Ast Copy ()
{
var copy = Alias != null
? new UsingStatementAst ( Extent , UsingStatementKind , Name , Alias )
: new UsingStatementAst ( Extent , UsingStatementKind , Name );
copy . ModuleInfo = ModuleInfo ;
return copy ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
var visitor2 = visitor as ICustomAstVisitor2 ;
return visitor2 != null ? visitor2 . VisitUsingStatement ( this ) : null ;
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = AstVisitAction . Continue ;
var visitor2 = visitor as AstVisitor2 ;
if ( visitor2 != null )
{
action = visitor2 . VisitUsingStatement ( this );
if ( action != AstVisitAction . Continue )
return visitor . CheckForPostAction ( this , action );
}
if ( Name != null )
action = Name . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue )
return visitor . CheckForPostAction ( this , action );
if ( ModuleSpecification != null )
action = ModuleSpecification . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue )
return visitor . CheckForPostAction ( this , action );
if ( Alias != null )
action = Alias . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
throw new NotImplementedException ();
}
/// <summary>
/// Define imported module and all type definitions imported by this using statement.
/// </summary>
/// <param name="moduleInfo"></param>
/// <returns>return ExportedTypeTable for this module</returns>
internal ReadOnlyDictionary < string , TypeDefinitionAst > DefineImportedModule ( PSModuleInfo moduleInfo )
{
var types = moduleInfo . GetExportedTypeDefinitions ();
ModuleInfo = moduleInfo ;
return types ;
}
/// <summary>
/// Is UsingStatementKind Module or Assembly.
/// </summary>
/// <returns>true, if it is.</returns>
internal bool IsUsingModuleOrAssembly ()
{
return UsingStatementKind == UsingStatementKind . Assembly || UsingStatementKind == UsingStatementKind . Module ;
}
}
/// <summary>
/// An abstract base class for type members.
/// </summary>
abstract public class MemberAst : Ast
{
/// <summary>
/// Initialize the common fields of a type member.
/// </summary>
/// <param name="extent">The extent of the type member.</param>
protected MemberAst ( IScriptExtent extent ) : base ( extent )
{
}
/// <summary>
/// The name of the member. This property is never null.
/// </summary>
public abstract string Name { get ; }
internal abstract string GetTooltip ();
}
/// <summary>
/// The attributes for a property.
/// </summary>
[Flags]
public enum PropertyAttributes
{
/// <summary>No attributes specified.</summary>
None = 0x00 ,
/// <summary>The property is public.</summary>
Public = 0x01 ,
/// <summary>The property is private.</summary>
Private = 0x02 ,
/// <summary>The property is static.</summary>
Static = 0x10 ,
/// <summary>The property is a literal.</summary>
Literal = 0x20 ,
/// <summary>The property is a hidden.</summary>
Hidden = 0x40 ,
}
/// <summary>
/// The ast for a property.
/// </summary>
public class PropertyMemberAst : MemberAst
{
private static readonly ReadOnlyCollection < AttributeAst > EmptyAttributeList =
Utils . EmptyReadOnlyCollection < AttributeAst >();
/// <summary>
/// Construct a property member.
/// </summary>
/// <param name="extent">The extent of the property starting with any custom attributes.</param>
/// <param name="name">The name of the property.</param>
/// <param name="propertyType">The ast for the type of the property - may be null.</param>
/// <param name="attributes">The custom attributes for the property.</param>
/// <param name="propertyAttributes">The attributes (like public or static) for the property.</param>
/// <param name="initialValue">The initial value of the property (may be null).</param>
public PropertyMemberAst ( IScriptExtent extent , string name , TypeConstraintAst propertyType , IEnumerable < AttributeAst > attributes , PropertyAttributes propertyAttributes , ExpressionAst initialValue )
: base ( extent )
{
if ( string . IsNullOrWhiteSpace ( name ))
{
throw PSTraceSource . NewArgumentNullException ( "name" );
}
if (( propertyAttributes & ( PropertyAttributes . Private | PropertyAttributes . Public )) ==
( PropertyAttributes . Private | PropertyAttributes . Public ))
{
throw PSTraceSource . NewArgumentException ( "propertyAttributes" );
}
_name = name ;
if ( propertyType != null )
{
PropertyType = propertyType ;
SetParent ( PropertyType );
}
if ( attributes != null )
{
this . Attributes = new ReadOnlyCollection < AttributeAst >( attributes . ToArray ());
SetParents ( Attributes );
}
else
{
this . Attributes = EmptyAttributeList ;
}
PropertyAttributes = propertyAttributes ;
InitialValue = initialValue ;
if ( InitialValue != null )
{
SetParent ( InitialValue );
}
}
/// <summary>
/// The name of the property.
/// </summary>
public override string Name { get { return _name ; } }
private readonly string _name ;
/// <summary>
/// The ast for the type of the property. This property may be null if no type was specified.
/// </summary>
public TypeConstraintAst PropertyType { get ; private set ; }
/// <summary>
/// The custom attributes of the property. This property is never null.
/// </summary>
public ReadOnlyCollection < AttributeAst > Attributes { get ; private set ; }
/// <summary>
/// The attributes (like public or static) of the property.
/// </summary>
public PropertyAttributes PropertyAttributes { get ; private set ; }
/// <summary>
/// The ast for the initial value of the property. This property may be null if no initial value was specified.
/// </summary>
public ExpressionAst InitialValue { get ; private set ; }
/// <summary>
/// Return true if the property is public.
/// </summary>
public bool IsPublic { get { return ( PropertyAttributes & PropertyAttributes . Public ) != 0 ; } }
/// <summary>
/// Return true if the property is private.
/// </summary>
public bool IsPrivate { get { return ( PropertyAttributes & PropertyAttributes . Private ) != 0 ; } }
/// <summary>
/// Return true if the property is hidden.
/// </summary>
public bool IsHidden { get { return ( PropertyAttributes & PropertyAttributes . Hidden ) != 0 ; } }
/// <summary>
/// Return true if the property is static.
/// </summary>
public bool IsStatic { get { return ( PropertyAttributes & PropertyAttributes . Static ) != 0 ; } }
/// <summary>
/// Copy the PropertyMemberAst.
/// </summary>
public override Ast Copy ()
{
var newPropertyType = CopyElement ( PropertyType );
var newAttributes = CopyElements ( Attributes );
var newInitialValue = CopyElement ( InitialValue );
return new PropertyMemberAst ( Extent , Name , newPropertyType , newAttributes , PropertyAttributes , newInitialValue );
}
internal override string GetTooltip ()
{
var type = PropertyType != null ? PropertyType . TypeName . FullName : "object" ;
return IsStatic
? "static " + type + " " + Name
: type + " " + Name ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
var visitor2 = visitor as ICustomAstVisitor2 ;
return visitor2 != null ? visitor2 . VisitPropertyMember ( this ) : null ;
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = AstVisitAction . Continue ;
var visitor2 = visitor as AstVisitor2 ;
if ( visitor2 != null )
{
action = visitor2 . VisitPropertyMember ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && PropertyType != null )
action = PropertyType . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Attributes . Count ; index ++)
{
var attributeAst = Attributes [ index ];
action = attributeAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue && InitialValue != null )
action = InitialValue . InternalVisit ( visitor );
}
return visitor . CheckForPostAction ( this , action );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
throw new NotImplementedException ();
}
#endregion Visitors
}
/// <summary>
/// Flags for a method.
/// </summary>
[Flags]
public enum MethodAttributes
{
/// <summary>No flags specified.</summary>
None = 0x00 ,
/// <summary>The method is public.</summary>
Public = 0x01 ,
/// <summary>The method is private.</summary>
Private = 0x02 ,
/// <summary>The method is static.</summary>
Static = 0x10 ,
/// <summary>The property is a hidden.</summary>
Hidden = 0x40 ,
}
/// <summary>
/// The ast for a method.
/// </summary>
public class FunctionMemberAst : MemberAst , IParameterMetadataProvider
{
private static readonly ReadOnlyCollection < AttributeAst > EmptyAttributeList =
Utils . EmptyReadOnlyCollection < AttributeAst >();
private static readonly ReadOnlyCollection < ParameterAst > EmptyParameterList =
Utils . EmptyReadOnlyCollection < ParameterAst >();
private readonly FunctionDefinitionAst _functionDefinitionAst ;
/// <summary>
/// Construct a member function.
/// </summary>
/// <param name="extent">The extent of the method starting from any attributes to the closing curly.</param>
/// <param name="functionDefinitionAst">The main body of the method.</param>
/// <param name="returnType">The return type of the method, may be null.</param>
/// <param name="attributes">The custom attributes for the function.</param>
/// <param name="methodAttributes">The method attributes like public or static.</param>
public FunctionMemberAst ( IScriptExtent extent , FunctionDefinitionAst functionDefinitionAst , TypeConstraintAst returnType , IEnumerable < AttributeAst > attributes , MethodAttributes methodAttributes )
: base ( extent )
{
if ( functionDefinitionAst == null )
{
throw PSTraceSource . NewArgumentNullException ( "functionDefinitionAst" );
}
if (( methodAttributes & ( MethodAttributes . Private | MethodAttributes . Public )) ==
( MethodAttributes . Private | MethodAttributes . Public ))
{
throw PSTraceSource . NewArgumentException ( "methodAttributes" );
}
if ( returnType != null )
{
ReturnType = returnType ;
SetParent ( returnType );
}
if ( attributes != null )
{
this . Attributes = new ReadOnlyCollection < AttributeAst >( attributes . ToArray ());
SetParents ( Attributes );
}
else
{
this . Attributes = EmptyAttributeList ;
}
_functionDefinitionAst = functionDefinitionAst ;
SetParent ( functionDefinitionAst );
MethodAttributes = methodAttributes ;
}
/// <summary>
/// The name of the method. This property is never null.
/// </summary>
public override string Name { get { return _functionDefinitionAst . Name ; } }
/// <summary>
/// The attributes specified on the method. This property is never null.
/// </summary>
public ReadOnlyCollection < AttributeAst > Attributes { get ; private set ; }
/// <summary>
/// The ast representing the return type for the method. This property may be null if no return type was specified.
/// </summary>
public TypeConstraintAst ReturnType { get ; private set ; }
/// <summary>
/// The parameters specified immediately after the function name. This property is never null.
/// </summary>
public ReadOnlyCollection < ParameterAst > Parameters {
get { return _functionDefinitionAst . Parameters ?? EmptyParameterList ; }
}
/// <summary>
/// The body of the function. This property is never null.
/// </summary>
public ScriptBlockAst Body { get { return _functionDefinitionAst . Body ; } }
/// <summary>
/// Method attribute flags.
/// </summary>
public MethodAttributes MethodAttributes { get ; private set ; }
/// <summary>
/// Returns true if the method is public.
/// </summary>
public bool IsPublic { get { return ( MethodAttributes & MethodAttributes . Public ) != 0 ; } }
/// <summary>
/// Returns true if the method is public.
/// </summary>
public bool IsPrivate { get { return ( MethodAttributes & MethodAttributes . Private ) != 0 ; } }
/// <summary>
/// Returns true if the method is hidden
/// </summary>
public bool IsHidden { get { return ( MethodAttributes & MethodAttributes . Hidden ) != 0 ; } }
/// <summary>
/// Returns true if the method is static
/// </summary>
public bool IsStatic { get { return ( MethodAttributes & MethodAttributes . Static ) != 0 ; } }
/// <summary>
/// Returns true if the method is a constructor
/// </summary>
public bool IsConstructor
{
get { return Name . Equals ((( TypeDefinitionAst ) Parent ). Name , StringComparison . OrdinalIgnoreCase ); }
}
internal IScriptExtent NameExtent { get { return _functionDefinitionAst . NameExtent ; } }
/// <summary>
/// Copy a function member ast.
/// </summary>
public override Ast Copy ()
{
var newDefn = CopyElement ( _functionDefinitionAst );
var newReturnType = CopyElement ( ReturnType );
var newAttributes = CopyElements ( Attributes );
return new FunctionMemberAst ( Extent , newDefn , newReturnType , newAttributes , MethodAttributes );
}
internal override string GetTooltip ()
{
var sb = new StringBuilder ();
if ( IsStatic )
{
sb . Append ( "static " );
}
sb . Append ( IsReturnTypeVoid () ? "void" : ReturnType . TypeName . FullName );
sb . Append ( ' ' );
sb . Append ( Name );
sb . Append ( '(' );
for ( int i = 0 ; i < Parameters . Count ; i ++)
{
if ( i > 0 )
{
sb . Append ( ", " );
}
sb . Append ( Parameters [ i ]. GetTooltip ());
}
sb . Append ( ')' );
return sb . ToString ();
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
var visitor2 = visitor as ICustomAstVisitor2 ;
return visitor2 != null ? visitor2 . VisitFunctionMember ( this ) : null ;
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = AstVisitAction . Continue ;
var visitor2 = visitor as AstVisitor2 ;
if ( visitor2 != null )
{
action = visitor2 . VisitFunctionMember ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Attributes . Count ; index ++)
{
var attributeAst = Attributes [ index ];
action = attributeAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue && ReturnType != null )
action = ReturnType . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = _functionDefinitionAst . InternalVisit ( visitor );
}
return visitor . CheckForPostAction ( this , action );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
throw new NotImplementedException ();
}
#endregion Visitors
#region IParameterMetadataProvider implementation
ReadOnlyCollection < ParameterAst > IParameterMetadataProvider . Parameters
{
get { return (( IParameterMetadataProvider ) _functionDefinitionAst ). Parameters ; }
}
RuntimeDefinedParameterDictionary IParameterMetadataProvider . GetParameterMetadata ( bool automaticPositions , ref bool usesCmdletBinding )
{
return (( IParameterMetadataProvider ) _functionDefinitionAst ). GetParameterMetadata ( automaticPositions , ref usesCmdletBinding );
}
IEnumerable < Attribute > IParameterMetadataProvider . GetScriptBlockAttributes ()
{
return (( IParameterMetadataProvider ) _functionDefinitionAst ). GetScriptBlockAttributes ();
}
bool IParameterMetadataProvider . UsesCmdletBinding ()
{
return (( IParameterMetadataProvider ) _functionDefinitionAst ). UsesCmdletBinding ();
}
PowerShell IParameterMetadataProvider . GetPowerShell ( ExecutionContext context , Dictionary < string , object > variables , bool isTrustedInput ,
bool filterNonUsingVariables , bool? createLocalScope , params object [] args )
{
// OK? I think this isn't reachable
throw new NotSupportedException ();
}
string IParameterMetadataProvider . GetWithInputHandlingForInvokeCommand ()
{
// OK? I think this isn't reachable
throw new NotSupportedException ();
}
Tuple < string , string > IParameterMetadataProvider . GetWithInputHandlingForInvokeCommandWithUsingExpression ( Tuple < List < VariableExpressionAst >, string > usingVariablesTuple )
{
throw new NotImplementedException ();
}
#endregion IParameterMetadataProvider implementation
#region Internal helpers
internal bool IsReturnTypeVoid ()
{
if ( ReturnType == null )
return true ;
var typeName = ReturnType . TypeName as TypeName ;
return typeName == null ? false : typeName . IsType ( typeof ( void ));
}
internal Type GetReturnType ()
{
return ReturnType == null ? typeof ( void ) : ReturnType . TypeName . GetReflectionType ();
}
#endregion
}
internal enum SpecialMemberFunctionType
{
None ,
DefaultConstructor ,
StaticConstructor ,
}
internal class CompilerGeneratedMemberFunctionAst : MemberAst , IParameterMetadataProvider
{
internal CompilerGeneratedMemberFunctionAst ( IScriptExtent extent , TypeDefinitionAst definingType , SpecialMemberFunctionType type )
: base ( extent )
{
StatementAst statement = null ;
if ( type == SpecialMemberFunctionType . DefaultConstructor )
{
var invokeMemberAst = new BaseCtorInvokeMemberExpressionAst ( extent , extent , Utils . EmptyArray < ExpressionAst >());
statement = new CommandExpressionAst ( extent , invokeMemberAst , null );
}
Body = new ScriptBlockAst ( extent , null , new StatementBlockAst ( extent , statement == null ? null : new [] { statement }, null ), false );
this . SetParent ( Body );
definingType . SetParent ( this );
DefiningType = definingType ;
Type = type ;
}
public override string Name
{
// This is fine for now, but if we add non-constructors, the name will be wrong.
get { return DefiningType . Name ; }
}
internal TypeDefinitionAst DefiningType { get ; private set ; }
internal SpecialMemberFunctionType Type { get ; private set ; }
internal override string GetTooltip ()
{
return DefiningType . Name + " new()" ;
}
public override Ast Copy ()
{
return new CompilerGeneratedMemberFunctionAst ( Extent , ( TypeDefinitionAst ) DefiningType . Copy (), Type );
}
internal override object Accept ( ICustomAstVisitor visitor )
{
Diagnostics . Assert ( false , "code should be unreachable" );
return null ;
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
Diagnostics . Assert ( false , "code should be unreachable" );
return AstVisitAction . Continue ;
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
Diagnostics . Assert ( false , "code should be unreachable" );
return Ast . EmptyPSTypeNameArray ;
}
public RuntimeDefinedParameterDictionary GetParameterMetadata ( bool automaticPositions , ref bool usesCmdletBinding )
{
return new RuntimeDefinedParameterDictionary { Data = RuntimeDefinedParameterDictionary . EmptyParameterArray };
}
public IEnumerable < Attribute > GetScriptBlockAttributes ()
{
return (( IParameterMetadataProvider ) Body ). GetScriptBlockAttributes ();
}
public bool UsesCmdletBinding ()
{
return false ;
}
public ReadOnlyCollection < ParameterAst > Parameters { get { return null ; } }
public ScriptBlockAst Body { get ; private set ; }
public PowerShell GetPowerShell ( ExecutionContext context , Dictionary < string , object > variables , bool isTrustedInput ,
bool filterNonUsingVariables , bool? createLocalScope , params object [] args )
{
Diagnostics . Assert ( false , "code should be unreachable" );
return null ;
}
public string GetWithInputHandlingForInvokeCommand ()
{
Diagnostics . Assert ( false , "code should be unreachable" );
return null ;
}
public Tuple < string , string > GetWithInputHandlingForInvokeCommandWithUsingExpression ( Tuple < List < VariableExpressionAst >, string > usingVariablesTuple )
{
Diagnostics . Assert ( false , "code should be unreachable" );
return null ;
}
}
/// <summary>
/// The ast that represents a function or filter definition. The function is always named.
/// </summary>
public class FunctionDefinitionAst : StatementAst , IParameterMetadataProvider
{
/// <summary>
/// Construct a function definition.
/// </summary>
/// <param name="extent">
/// The extent of the function definition, starting with the function or filter keyword, ending at the closing curly.
/// </param>
/// <param name="isFilter">True if the filter keyword was used.</param>
/// <param name="isWorkflow">True if the workflow keyword was used.</param>
/// <param name="name">The name of the function.</param>
/// <param name="parameters">
/// The parameters specified after the function name. This does not include parameters specified with a param statement.
/// </param>
/// <param name="body">The body of the function/filter.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="name"/>, or <paramref name="body"/> is null, or
/// if <paramref name="name"/> is an empty string.
/// </exception>
public FunctionDefinitionAst ( IScriptExtent extent ,
bool isFilter ,
bool isWorkflow ,
string name ,
IEnumerable < ParameterAst > parameters ,
ScriptBlockAst body )
: base ( extent )
{
if ( string . IsNullOrEmpty ( name ))
{
throw PSTraceSource . NewArgumentNullException ( "name" );
}
if ( body == null )
{
throw PSTraceSource . NewArgumentNullException ( "body" );
}
if ( isFilter && isWorkflow )
{
throw PSTraceSource . NewArgumentException ( "isFilter" );
}
this . IsFilter = isFilter ;
this . IsWorkflow = isWorkflow ;
this . Name = name ;
if ( parameters != null && parameters . Any ())
{
this . Parameters = new ReadOnlyCollection < ParameterAst >( parameters . ToArray ());
SetParents ( Parameters );
}
this . Body = body ;
SetParent ( body );
}
internal FunctionDefinitionAst ( IScriptExtent extent ,
bool isFilter ,
bool isWorkflow ,
Token functionNameToken ,
IEnumerable < ParameterAst > parameters ,
ScriptBlockAst body )
: this ( extent ,
isFilter ,
isWorkflow ,
( functionNameToken . Kind == TokenKind . Generic ) ? (( StringToken ) functionNameToken ). Value : functionNameToken . Text ,
parameters ,
body )
{
NameExtent = functionNameToken . Extent ;
}
/// <summary>
/// If true, the filter keyword was used.
/// </summary>
public bool IsFilter { get ; private set ; }
/// <summary>
/// If true, the workflow keyword was used.
/// </summary>
public bool IsWorkflow { get ; private set ; }
/// <summary>
/// The name of the function or filter. This property is never null or empty.
/// </summary>
public string Name { get ; private set ; }
/// <summary>
/// The parameters specified immediately after the function name, or null if no parameters were specified.
/// <para>It is possible that this property may have a value and <see cref="ScriptBlockAst.ParamBlock"/> to also have a
/// value. Normally this is not allowed in a valid script, but in one rare case it is allowed:</para>
/// <c>function foo() { param($a) }</c>
/// <para>
/// In this example, the parameters specified after the function name must be empty or the script is not valid.
/// </para>
/// </summary>
public ReadOnlyCollection < ParameterAst > Parameters { get ; private set ; }
/// <summary>
/// The body of the function. This property is never null.
/// </summary>
public ScriptBlockAst Body { get ; private set ; }
internal IScriptExtent NameExtent { get ; private set ; }
/// <summary>
/// Return the help content, if any, for the function.
/// </summary>
public CommentHelpInfo GetHelpContent ()
{
Dictionary < Ast , Token []> scriptBlockTokenCache = new Dictionary < Ast , Token []>();
var commentTokens = HelpCommentsParser . GetHelpCommentTokens ( this , scriptBlockTokenCache );
if ( commentTokens != null )
{
return HelpCommentsParser . GetHelpContents ( commentTokens . Item1 , commentTokens . Item2 );
}
return null ;
}
/// <summary>
/// Return the help content, if any, for the function.
/// Use this overload when parsing multiple functions within a single scope.
/// </summary>
/// <param name="scriptBlockTokenCache">A dictionary that the parser will use to
/// map AST nodes to their respective tokens. The parser uses this to improve performance
/// while repeatedly parsing the parent script blocks of a function (since the parent
/// script blocks may contain help comments related to this function.
/// To conserve memory, clear / null-out this cache when done with repeated parsing.</param>
/// <returns></returns>
public CommentHelpInfo GetHelpContent ( Dictionary < Ast , Token []> scriptBlockTokenCache )
{
if ( scriptBlockTokenCache == null )
{
throw new ArgumentNullException ( "scriptBlockTokenCache" );
}
var commentTokens = HelpCommentsParser . GetHelpCommentTokens ( this , scriptBlockTokenCache );
if ( commentTokens != null )
{
return HelpCommentsParser . GetHelpContents ( commentTokens . Item1 , commentTokens . Item2 );
}
return null ;
}
/// <summary>
/// Copy the FunctionDefinitionAst instance
/// </summary>
public override Ast Copy ()
{
var newParameters = CopyElements ( this . Parameters );
var newBody = CopyElement ( this . Body );
return new FunctionDefinitionAst ( this . Extent , this . IsFilter , this . IsWorkflow , this . Name , newParameters , newBody ) { NameExtent = this . NameExtent };
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
internal string GetParamTextFromParameterList ( Tuple < List < VariableExpressionAst >, string > usingVariablesTuple = null )
{
Diagnostics . Assert ( Parameters != null , "Caller makes sure that Parameters is not null before calling this method." );
string additionalNewUsingParams = null ;
IEnumerator < VariableExpressionAst > orderedUsingVars = null ;
if ( usingVariablesTuple != null )
{
Diagnostics . Assert (
usingVariablesTuple . Item1 != null && usingVariablesTuple . Item1 . Count > 0 && ! string . IsNullOrEmpty ( usingVariablesTuple . Item2 ),
"Caller makes sure the value passed in is not null or empty." );
orderedUsingVars = usingVariablesTuple . Item1 . OrderBy ( varAst => varAst . Extent . StartOffset ). GetEnumerator ();
additionalNewUsingParams = usingVariablesTuple . Item2 ;
}
var sb = new StringBuilder ( "param(" );
string separator = "" ;
if ( additionalNewUsingParams != null )
{
// Add the $using variable parameters if necessary
sb . Append ( additionalNewUsingParams );
separator = ", " ;
}
for ( int i = 0 ; i < Parameters . Count ; i ++)
{
var param = Parameters [ i ];
sb . Append ( separator );
sb . Append ( orderedUsingVars != null
? param . GetParamTextWithDollarUsingHandling ( orderedUsingVars )
: param . ToString ());
separator = ", " ;
}
sb . Append ( ")" );
sb . Append ( Environment . NewLine );
return sb . ToString ();
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitFunctionDefinition ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitFunctionDefinition ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
if ( Parameters != null )
{
for ( int index = 0 ; index < Parameters . Count ; index ++)
{
var param = Parameters [ index ];
action = param . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
#region IParameterMetadataProvider implementation
RuntimeDefinedParameterDictionary IParameterMetadataProvider . GetParameterMetadata ( bool automaticPositions , ref bool usesCmdletBinding )
{
if ( Parameters != null )
{
return Compiler . GetParameterMetaData ( Parameters , automaticPositions , ref usesCmdletBinding );
}
if ( Body . ParamBlock != null )
{
return Compiler . GetParameterMetaData ( Body . ParamBlock . Parameters , automaticPositions , ref usesCmdletBinding );
}
return new RuntimeDefinedParameterDictionary { Data = RuntimeDefinedParameterDictionary . EmptyParameterArray };
}
IEnumerable < Attribute > IParameterMetadataProvider . GetScriptBlockAttributes ()
{
return (( IParameterMetadataProvider ) Body ). GetScriptBlockAttributes ();
}
ReadOnlyCollection < ParameterAst > IParameterMetadataProvider . Parameters
{
get { return Parameters ?? ( Body . ParamBlock != null ? Body . ParamBlock . Parameters : null ); }
}
PowerShell IParameterMetadataProvider . GetPowerShell ( ExecutionContext context , Dictionary < string , object > variables , bool isTrustedInput ,
bool filterNonUsingVariables , bool? createLocalScope , params object [] args )
{
ExecutionContext . CheckStackDepth ();
return ScriptBlockToPowerShellConverter . Convert ( this . Body , this . Parameters , isTrustedInput , context , variables , filterNonUsingVariables , createLocalScope , args );
}
string IParameterMetadataProvider . GetWithInputHandlingForInvokeCommand ()
{
string result = (( IParameterMetadataProvider ) Body ). GetWithInputHandlingForInvokeCommand ();
return Parameters == null ? result : ( GetParamTextFromParameterList () + result );
}
Tuple < string , string > IParameterMetadataProvider . GetWithInputHandlingForInvokeCommandWithUsingExpression (
Tuple < List < VariableExpressionAst >, string > usingVariablesTuple )
{
Tuple < string , string > result =
(( IParameterMetadataProvider ) Body ). GetWithInputHandlingForInvokeCommandWithUsingExpression ( usingVariablesTuple );
if ( Parameters == null )
{
return result ;
}
string paramText = GetParamTextFromParameterList ( usingVariablesTuple );
return new Tuple < string , string >( paramText , result . Item2 );
}
bool IParameterMetadataProvider . UsesCmdletBinding ()
{
bool usesCmdletBinding = false ;
if ( Parameters != null )
{
usesCmdletBinding = ParamBlockAst . UsesCmdletBinding ( Parameters );
}
else if ( Body . ParamBlock != null )
{
usesCmdletBinding = (( IParameterMetadataProvider ) Body ). UsesCmdletBinding ();
}
return usesCmdletBinding ;
}
#endregion IParameterMetadataProvider implementation
}
/// <summary>
/// The ast that represents an if statement.
/// </summary>
public class IfStatementAst : StatementAst
{
/// <summary>
/// Construct an if statement.
/// </summary>
/// <param name="extent">
/// The extent of the statement, starting with the if keyword, ending at the closing curly of the last clause.
/// </param>
/// <param name="clauses">
/// A non-empty collection of pairs of condition expressions and statement blocks.
/// </param>
/// <param name="elseClause">The else clause, or null if no clause was specified.</param>
/// <exception cref="PSArgumentNullException">If <paramref name="extent"/> is null.</exception>
/// <exception cref="PSArgumentException">If <paramref name="clauses"/> is null or empty.</exception>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public IfStatementAst ( IScriptExtent extent , IEnumerable < IfClause > clauses , StatementBlockAst elseClause )
: base ( extent )
{
if ( clauses == null || ! clauses . Any ())
{
throw PSTraceSource . NewArgumentException ( "clauses" );
}
this . Clauses = new ReadOnlyCollection < IfClause >( clauses . ToArray ());
SetParents ( Clauses );
if ( elseClause != null )
{
this . ElseClause = elseClause ;
SetParent ( elseClause );
}
}
/// <summary>
/// The asts representing a pair of (condition,statements) that are tested, in sequence until the first condition
/// tests true, in which case it's statements are executed, otherwise the <see cref="ElseClause"/>, if any, is
/// executed. This property is never null and always has at least 1 value.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public ReadOnlyCollection < IfClause > Clauses { get ; private set ; }
/// <summary>
/// The ast for the else clause, or null if no else clause is specified.
/// </summary>
public StatementBlockAst ElseClause { get ; private set ; }
/// <summary>
/// Copy the IfStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newClauses = new List < IfClause >( this . Clauses . Count );
for ( int i = 0 ; i < this . Clauses . Count ; i ++)
{
var clause = this . Clauses [ i ];
var newCondition = CopyElement ( clause . Item1 );
var newStatementBlock = CopyElement ( clause . Item2 );
newClauses . Add ( Tuple . Create ( newCondition , newStatementBlock ));
}
var newElseClause = CopyElement ( this . ElseClause );
return new IfStatementAst ( this . Extent , newClauses , newElseClause );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
foreach ( var typename in Clauses . SelectMany ( clause => clause . Item2 . GetInferredType ( context )))
{
yield return typename ;
}
if ( ElseClause != null )
{
foreach ( var typename in ElseClause . GetInferredType ( context ))
{
yield return typename ;
}
}
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitIfStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitIfStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Clauses . Count ; index ++)
{
var ifClause = Clauses [ index ];
action = ifClause . Item1 . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
action = ifClause . Item2 . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue && ElseClause != null )
{
action = ElseClause . InternalVisit ( visitor );
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast representing the data statement.
/// </summary>
public class DataStatementAst : StatementAst
{
private static readonly ExpressionAst [] EmptyCommandsAllowed = Utils . EmptyArray < ExpressionAst >();
/// <summary>
/// Construct a data statement.
/// </summary>
/// <param name="extent">The extent of the data statement, extending from the data keyword to the closing curly brace.</param>
/// <param name="variableName">The name of the variable, if specified, otherwise null.</param>
/// <param name="commandsAllowed">The list of commands allowed in the data statement, if specified, otherwise null.</param>
/// <param name="body">The body of the data statement.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="body"/> is null.
/// </exception>
public DataStatementAst ( IScriptExtent extent ,
string variableName ,
IEnumerable < ExpressionAst > commandsAllowed ,
StatementBlockAst body )
: base ( extent )
{
if ( body == null )
{
throw PSTraceSource . NewArgumentNullException ( "body" );
}
if ( string . IsNullOrWhiteSpace ( variableName ))
{
variableName = null ;
}
this . Variable = variableName ;
if ( commandsAllowed != null && commandsAllowed . Any ())
{
this . CommandsAllowed = new ReadOnlyCollection < ExpressionAst >( commandsAllowed . ToArray ());
SetParents ( CommandsAllowed );
this . HasNonConstantAllowedCommand = CommandsAllowed . Any ( ast => !( ast is StringConstantExpressionAst ));
}
else
{
this . CommandsAllowed = new ReadOnlyCollection < ExpressionAst >( EmptyCommandsAllowed );
}
this . Body = body ;
SetParent ( body );
}
/// <summary>
/// The name of the variable this data statement sets, or null if no variable name was specified.
/// </summary>
public string Variable { get ; private set ; }
/// <summary>
/// The asts naming the commands allowed to execute in this data statement.
/// </summary>
public ReadOnlyCollection < ExpressionAst > CommandsAllowed { get ; private set ; }
/// <summary>
/// The ast for the body of the data statement. This property is never null.
/// </summary>
public StatementBlockAst Body { get ; private set ; }
/// <summary>
/// Copy the DataStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newCommandsAllowed = CopyElements ( this . CommandsAllowed );
var newBody = CopyElement ( this . Body );
return new DataStatementAst ( this . Extent , this . Variable , newCommandsAllowed , newBody );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Body . GetInferredType ( context );
}
internal bool HasNonConstantAllowedCommand { get ; private set ; }
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitDataStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitDataStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
#region Code Generation Details
private int _tupleIndex = VariableAnalysis . Unanalyzed ;
internal int TupleIndex
{
get { return _tupleIndex ; }
set { _tupleIndex = value ; }
}
#endregion Code Generation Details
}
#region Looping Statements
/// <summary>
/// An abstract base class for statements that have labels such as a while statement or a switch statement.
/// </summary>
public abstract class LabeledStatementAst : StatementAst
{
/// <summary>
/// Initialize the properties commmon to labeled statements.
/// </summary>
/// <param name="extent">The extent of the statement.</param>
/// <param name="label">The optionally null label for the statement.</param>
/// <param name="condition">The optionally null pipeline for the condition test of the statement.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
protected LabeledStatementAst ( IScriptExtent extent , string label , PipelineBaseAst condition )
: base ( extent )
{
if ( string . IsNullOrWhiteSpace ( label ))
{
label = null ;
}
this . Label = label ;
if ( condition != null )
{
this . Condition = condition ;
SetParent ( condition );
}
}
/// <summary>
/// The label name if specified, otherwise null.
/// </summary>
public string Label { get ; private set ; }
/// <summary>
/// The ast for the condition that is tested on each iteration of the loop, or the condition tested on a switch.
/// This property may be null if the statement is a <see cref="ForStatementAst"/>, otherwise it is never null.
/// </summary>
public PipelineBaseAst Condition { get ; private set ; }
}
/// <summary>
/// An abstract base class for looping statements including a the do/while statement, the do/until statement,
/// the foreach statement, the for statement, and the while statement.
/// </summary>
public abstract class LoopStatementAst : LabeledStatementAst
{
/// <summary>
/// Initialize the properties common to all loop statements.
/// </summary>
/// <param name="extent">The extent of the statement.</param>
/// <param name="label">The optionally null label for the statement.</param>
/// <param name="condition">The optionally null pipeline for the condition test of the statement.</param>
/// <param name="body">The body of the statement.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="body"/> is null.
/// </exception>
protected LoopStatementAst ( IScriptExtent extent , string label , PipelineBaseAst condition , StatementBlockAst body )
: base ( extent , label , condition )
{
if ( body == null )
{
throw PSTraceSource . NewArgumentNullException ( "body" );
}
this . Body = body ;
SetParent ( body );
}
/// <summary>
/// The body of a loop statement. This property is never null.
/// </summary>
public StatementBlockAst Body { get ; private set ; }
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Body . GetInferredType ( context );
}
}
/// <summary>
/// Flags that are specified on a foreach statement. Values may be or'ed together, not all invalid combinations
/// of flags are detected.
/// </summary>
[Flags]
public enum ForEachFlags
{
/// <summary>
/// No flags specified.
/// </summary>
None = 0x00 ,
/// <summary>
/// The -parallel flag.
/// </summary>
Parallel = 0x01 ,
// If any flags are added that impact evaluation of items during the foreach statement, then
// a binder (and caching strategy) needs to be added similar to SwitchClauseEvalBinder.
}
/// <summary>
/// The ast representing the foreach statement.
/// </summary>
public class ForEachStatementAst : LoopStatementAst
{
/// <summary>
/// Construct a foreach statement.
/// </summary>
/// <param name="extent">
/// The extent of the statement, starting from the optional label or the foreach keyword and ending at the closing curly brace.
/// </param>
/// <param name="label">The optionally null label.</param>
/// <param name="flags">Any flags that affect how the foreach statement is processed.</param>
/// <param name="variable">The variable set on each iteration of the loop.</param>
/// <param name="expression">The pipeline generating values to iterate through.</param>
/// <param name="body">The body to execute for each element written from pipeline.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="expression"/>, or <paramref name="variable"/> is null.
/// </exception>
public ForEachStatementAst ( IScriptExtent extent ,
string label ,
ForEachFlags flags ,
VariableExpressionAst variable ,
PipelineBaseAst expression ,
StatementBlockAst body )
: base ( extent , label , expression , body )
{
if ( expression == null || variable == null )
{
throw PSTraceSource . NewArgumentNullException ( expression == null ? "expression" : "variablePath" );
}
this . Flags = flags ;
this . Variable = variable ;
SetParent ( variable );
}
/// <summary>
/// Construct a foreach statement.
/// </summary>
/// <param name="extent">
/// The extent of the statement, starting from the optional label or the foreach keyword and ending at the closing curly brace.
/// </param>
/// <param name="label">The optionally null label.</param>
/// <param name="flags">Any flags that affect how the foreach statement is processed.</param>
/// <param name="throttleLimit">The limit to be obeyed during parallel processing, if any.</param>
/// <param name="variable">The variable set on each iteration of the loop.</param>
/// <param name="expression">The pipeline generating values to iterate through.</param>
/// <param name="body">The body to execute for each element written from pipeline.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="expression"/>, or <paramref name="variable"/> is null.
/// </exception>
public ForEachStatementAst ( IScriptExtent extent ,
string label ,
ForEachFlags flags ,
ExpressionAst throttleLimit ,
VariableExpressionAst variable ,
PipelineBaseAst expression ,
StatementBlockAst body )
: this ( extent , label , flags , variable , expression , body )
{
this . ThrottleLimit = throttleLimit ;
if ( throttleLimit != null )
{
SetParent ( throttleLimit );
}
}
/// <summary>
/// The name of the variable set for each item as the loop iterates. This property is never null.
/// </summary>
public VariableExpressionAst Variable { get ; private set ; }
/// <summary>
/// The limit to be obeyed during parallel processing, if any.
/// </summary>
public ExpressionAst ThrottleLimit { get ; private set ; }
/// <summary>
/// The flags, if any specified on the foreach statement.
/// </summary>
public ForEachFlags Flags { get ; private set ; }
/// <summary>
/// Copy the ForEachStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newVariable = CopyElement ( this . Variable );
var newExpression = CopyElement ( this . Condition );
var newBody = CopyElement ( this . Body );
if ( this . ThrottleLimit != null )
{
var newThrottleLimit = CopyElement ( this . ThrottleLimit );
return new ForEachStatementAst ( this . Extent , this . Label , this . Flags , newThrottleLimit ,
newVariable , newExpression , newBody );
}
return new ForEachStatementAst ( this . Extent , this . Label , this . Flags , newVariable , newExpression , newBody );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitForEachStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitForEachStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Variable . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Condition . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast for a for statement.
/// </summary>
public class ForStatementAst : LoopStatementAst
{
/// <summary>
/// Construct a for statement.
/// </summary>
/// <param name="extent">The extent of the statement, from the label or for keyword to the closing curly.</param>
/// <param name="label">The optionally null label.</param>
/// <param name="initializer">The optionally null initialization expression executed before the loop.</param>
/// <param name="condition">The optionally null condition expression tested on each iteration of the loop.</param>
/// <param name="iterator">The optionally null iteration expression executed after each iteration of the loop.</param>
/// <param name="body">The body executed on each iteration of the loop.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public ForStatementAst ( IScriptExtent extent ,
string label ,
PipelineBaseAst initializer ,
PipelineBaseAst condition ,
PipelineBaseAst iterator ,
StatementBlockAst body )
: base ( extent , label , condition , body )
{
if ( initializer != null )
{
this . Initializer = initializer ;
SetParent ( initializer );
}
if ( iterator != null )
{
this . Iterator = iterator ;
SetParent ( iterator );
}
}
/// <summary>
/// The ast for the initialization expression of a for statement, or null if none was specified.
/// </summary>
public PipelineBaseAst Initializer { get ; private set ; }
/// <summary>
/// The ast for the iteration experssion of a for statement, or null if none was specified.
/// </summary>
public PipelineBaseAst Iterator { get ; private set ; }
/// <summary>
/// Copy the ForStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newInitializer = CopyElement ( this . Initializer );
var newCondition = CopyElement ( this . Condition );
var newIterator = CopyElement ( this . Iterator );
var newBody = CopyElement ( this . Body );
return new ForStatementAst ( this . Extent , this . Label , newInitializer , newCondition , newIterator , newBody );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitForStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitForStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && Initializer != null )
action = Initializer . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue && Condition != null )
action = Condition . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue && Iterator != null )
action = Iterator . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents the do/while statement.
/// </summary>
public class DoWhileStatementAst : LoopStatementAst
{
/// <summary>
/// Construct a do/while statement.
/// </summary>
/// <param name="extent">The extent of the do/while statment from the label or do keyword to the closing curly brace.</param>
/// <param name="label">The optionally null label.</param>
/// <param name="condition">The condition tested on each iteration of the loop.</param>
/// <param name="body">The body executed on each iteration of the loop.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="condition"/> is null.
/// </exception>
public DoWhileStatementAst ( IScriptExtent extent , string label , PipelineBaseAst condition , StatementBlockAst body )
: base ( extent , label , condition , body )
{
if ( condition == null )
{
throw PSTraceSource . NewArgumentNullException ( "condition" );
}
}
/// <summary>
/// Copy the DoWhileStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newCondition = CopyElement ( this . Condition );
var newBody = CopyElement ( this . Body );
return new DoWhileStatementAst ( this . Extent , this . Label , newCondition , newBody );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitDoWhileStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitDoWhileStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Condition . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents a do/until statement.
/// </summary>
public class DoUntilStatementAst : LoopStatementAst
{
/// <summary>
/// Construct a do/until statement.
/// </summary>
/// <param name="extent">The extent of the statement, from the label or do keyword to the closing curly brace.</param>
/// <param name="label">The optionally null label.</param>
/// <param name="condition">The condition tested on each iteration of the loop.</param>
/// <param name="body">The body executed on each iteration of the loop.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="condition"/> is null.
/// </exception>
public DoUntilStatementAst ( IScriptExtent extent , string label , PipelineBaseAst condition , StatementBlockAst body )
: base ( extent , label , condition , body )
{
if ( condition == null )
{
throw PSTraceSource . NewArgumentNullException ( "condition" );
}
}
/// <summary>
/// Copy the DoUntilStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newCondition = CopyElement ( this . Condition );
var newBody = CopyElement ( this . Body );
return new DoUntilStatementAst ( this . Extent , this . Label , newCondition , newBody );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitDoUntilStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitDoUntilStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Condition . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast for a while statement.
/// </summary>
public class WhileStatementAst : LoopStatementAst
{
/// <summary>
/// Construct a while statement.
/// </summary>
/// <param name="extent">The extent of the statement, from the label or while keyword to the closing curly brace.</param>
/// <param name="label">The optionally null label.</param>
/// <param name="condition">The condition tested on each iteration of the loop.</param>
/// <param name="body">The body executed on each iteration of the loop.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="condition"/> is null.
/// </exception>
public WhileStatementAst ( IScriptExtent extent , string label , PipelineBaseAst condition , StatementBlockAst body )
: base ( extent , label , condition , body )
{
if ( condition == null )
{
throw PSTraceSource . NewArgumentNullException ( "condition" );
}
}
/// <summary>
/// Copy the WhileStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newCondition = CopyElement ( this . Condition );
var newBody = CopyElement ( this . Body );
return new WhileStatementAst ( this . Extent , this . Label , newCondition , newBody );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitWhileStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitWhileStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Condition . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// Flags that are specified on a switch statement. Values may be or'ed together, not all invalid combinations
/// of flags are detected.
/// </summary>
[Flags]
public enum SwitchFlags
{
/// <summary>
/// No flags specified.
/// </summary>
None = 0x00 ,
/// <summary>
/// The -file flag.
/// </summary>
File = 0x01 ,
/// <summary>
/// The -regex flag.
/// </summary>
Regex = 0x02 ,
/// <summary>
/// The -wildcard flag.
/// </summary>
Wildcard = 0x04 ,
/// <summary>
/// The -exact flag.
/// </summary>
Exact = 0x08 ,
/// <summary>
/// The -casesensitive flag.
/// </summary>
CaseSensitive = 0x10 ,
/// <summary>
/// The -parallel flag.
/// </summary>
Parallel = 0x20 ,
// If any flags are added that influence evaluation of switch elements,
// then the caching strategy in SwitchClauseEvalBinder needs to be updated,
// and possibly its _binderCache.
}
/// <summary>
/// The ast that represents a switch statement.
/// </summary>
public class SwitchStatementAst : LabeledStatementAst
{
private static readonly SwitchClause [] EmptyClauseArray = Utils . EmptyArray < SwitchClause >();
/// <summary>
/// Construct a switch statement.
/// </summary>
/// <param name="extent">The extent of the statement, from the label or switch keyword to the closing curly.</param>
/// <param name="label">The optionally null label.</param>
/// <param name="condition">The expression being switched upon.</param>
/// <param name="flags">Any flags that affect how the <paramref name="condition"/> is tested.</param>
/// <param name="clauses">
/// A possibly null or empty collection of conditions and block of statements to execute if the condition matches.
/// </param>
/// <param name="default">The default clause to execute if no clauses match.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="default"/> and <paramref name="clauses"/> are both null or empty.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public SwitchStatementAst ( IScriptExtent extent ,
string label ,
PipelineBaseAst condition ,
SwitchFlags flags ,
IEnumerable < SwitchClause > clauses ,
StatementBlockAst @default )
: base ( extent , label , condition )
{
if (( clauses == null || ! clauses . Any ()) && @default == null )
{
// Must specify either clauses or default. If neither, just complain about clauses as that's the most likely
// invalid argument.
throw PSTraceSource . NewArgumentException ( "clauses" );
}
this . Flags = flags ;
this . Clauses = new ReadOnlyCollection < SwitchClause >(
( clauses != null && clauses . Any ()) ? clauses . ToArray () : EmptyClauseArray );
SetParents ( Clauses );
if ( @default != null )
{
this . Default = @default ;
SetParent ( @default );
}
}
/// <summary>
/// The flags, if any specified on the switch statement.
/// </summary>
public SwitchFlags Flags { get ; private set ; }
/// <summary>
/// A possibly empty collection of conditions and statement blocks representing the cases of the switch statement.
/// If the colleciton is empty, the default clause is not null.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public ReadOnlyCollection < SwitchClause > Clauses { get ; private set ; }
/// <summary>
/// The ast for the default of the switch statement, or null if no default block was specified.
/// </summary>
public StatementBlockAst Default { get ; private set ; }
/// <summary>
/// Copy the SwitchStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newCondition = CopyElement ( this . Condition );
var newDefault = CopyElement ( this . Default );
List < SwitchClause > newClauses = null ;
if ( this . Clauses . Count > 0 )
{
newClauses = new List < SwitchClause >( this . Clauses . Count );
for ( int i = 0 ; i < this . Clauses . Count ; i ++)
{
var clause = this . Clauses [ i ];
var newSwitchItem1 = CopyElement ( clause . Item1 );
var newSwitchItem2 = CopyElement ( clause . Item2 );
newClauses . Add ( Tuple . Create ( newSwitchItem1 , newSwitchItem2 ));
}
}
return new SwitchStatementAst ( this . Extent , this . Label , newCondition , this . Flags , newClauses , newDefault );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
foreach ( var typename in Clauses . SelectMany ( clause => clause . Item2 . GetInferredType ( context )))
{
yield return typename ;
}
if ( Default != null )
{
foreach ( var typename in Default . GetInferredType ( context ))
{
yield return typename ;
}
}
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitSwitchStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitSwitchStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Condition . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Clauses . Count ; index ++)
{
var switchClauseAst = Clauses [ index ];
action = switchClauseAst . Item1 . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
action = switchClauseAst . Item2 . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue && Default != null )
action = Default . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
#endregion Looping Statements
#region Exception Handling Statements
/// <summary>
/// The ast that represents a single catch as part of a try statement.
/// </summary>
public class CatchClauseAst : Ast
{
private readonly static ReadOnlyCollection < TypeConstraintAst > EmptyCatchTypes =
Utils . EmptyReadOnlyCollection < TypeConstraintAst >();
/// <summary>
/// Construct a catch clause.
/// </summary>
/// <param name="extent">The extent of the catch, from the catch keyword to the closing curly brace.</param>
/// <param name="catchTypes">The collection of types caught by this catch clause, may be null if all types are caught.</param>
/// <param name="body">The body of the catch clause.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="body"/> is null.
/// </exception>
public CatchClauseAst ( IScriptExtent extent , IEnumerable < TypeConstraintAst > catchTypes , StatementBlockAst body )
: base ( extent )
{
if ( body == null )
{
throw PSTraceSource . NewArgumentNullException ( "body" );
}
if ( catchTypes != null )
{
this . CatchTypes = new ReadOnlyCollection < TypeConstraintAst >( catchTypes . ToArray ());
SetParents ( CatchTypes );
}
else
{
this . CatchTypes = EmptyCatchTypes ;
}
this . Body = body ;
SetParent ( body );
}
/// <summary>
/// A possibly empty collection of types caught by this catch block. If the collection is empty, the catch handler
/// catches all exceptions.
/// </summary>
public ReadOnlyCollection < TypeConstraintAst > CatchTypes { get ; private set ; }
/// <summary>
/// Returns true if this handler handles any kind of exception.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "CatchAll")]
public bool IsCatchAll { get { return CatchTypes . Count == 0 ; } }
/// <summary>
/// The body of the catch block. This property is never null.
/// </summary>
public StatementBlockAst Body { get ; private set ; }
/// <summary>
/// Copy the CatchClauseAst instance
/// </summary>
public override Ast Copy ()
{
var newCatchTypes = CopyElements ( this . CatchTypes );
var newBody = CopyElement ( this . Body );
return new CatchClauseAst ( this . Extent , newCatchTypes , newBody );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Body . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitCatchClause ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitCatchClause ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
for ( int index = 0 ; index < CatchTypes . Count ; index ++)
{
var catchType = CatchTypes [ index ];
if ( action != AstVisitAction . Continue ) break ;
action = catchType . InternalVisit ( visitor );
}
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents a try statement.
/// </summary>
public class TryStatementAst : StatementAst
{
private readonly static ReadOnlyCollection < CatchClauseAst > EmptyCatchClauses =
Utils . EmptyReadOnlyCollection < CatchClauseAst >();
/// <summary>
/// Construct a try statement ast.
/// </summary>
/// <param name="extent">
/// The extent of the try statement, from the try keyword to the closing curly of the last catch or finally.
/// </param>
/// <param name="body">The region of guarded code.</param>
/// <param name="catchClauses">The list of catch clauses, may be null.</param>
/// <param name="finally">The finally clause, may be null.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="body"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="catchClauses"/> is null or is an empty collection and <paramref name="finally"/> is also
/// null, then an exception is also raised as the try block must have a finally or at least one catch.
/// </exception>
public TryStatementAst ( IScriptExtent extent ,
StatementBlockAst body ,
IEnumerable < CatchClauseAst > catchClauses ,
StatementBlockAst @finally )
: base ( extent )
{
if ( body == null )
{
throw PSTraceSource . NewArgumentNullException ( "body" );
}
if (( catchClauses == null || ! catchClauses . Any ()) && @finally == null )
{
// If no catches and no finally, just complain about catchClauses as that's the most likely invalid argument.
throw PSTraceSource . NewArgumentException ( "catchClauses" );
}
this . Body = body ;
SetParent ( body );
if ( catchClauses != null )
{
this . CatchClauses = new ReadOnlyCollection < CatchClauseAst >( catchClauses . ToArray ());
SetParents ( CatchClauses );
}
else
{
this . CatchClauses = EmptyCatchClauses ;
}
if ( @finally != null )
{
this . Finally = @finally ;
SetParent ( @finally );
}
}
/// <summary>
/// The body of the try statement. This property is never null.
/// </summary>
public StatementBlockAst Body { get ; private set ; }
/// <summary>
/// A collection of catch clauses, which is empty if there are no catches.
/// </summary>
public ReadOnlyCollection < CatchClauseAst > CatchClauses { get ; private set ; }
/// <summary>
/// The ast for the finally block, or null if no finally block was specified, in which case <see cref="CatchClauses"/>
/// is a non-null, non-empty collection.
/// </summary>
public StatementBlockAst Finally { get ; private set ; }
/// <summary>
/// Copy the TryStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newBody = CopyElement ( this . Body );
var newCatchClauses = CopyElements ( this . CatchClauses );
var newFinally = CopyElement ( this . Finally );
return new TryStatementAst ( this . Extent , newBody , newCatchClauses , newFinally );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
foreach ( var typename in Body . GetInferredType ( context ))
{
yield return typename ;
}
foreach ( var typename in CatchClauses . SelectMany ( clause => clause . Body . GetInferredType ( context )))
{
yield return typename ;
}
if ( Finally != null )
{
foreach ( var typename in Finally . GetInferredType ( context ))
{
yield return typename ;
}
}
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitTryStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitTryStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < CatchClauses . Count ; index ++)
{
var catchClause = CatchClauses [ index ];
action = catchClause . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue && Finally != null )
action = Finally . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents the trap statement.
/// </summary>
public class TrapStatementAst : StatementAst
{
/// <summary>
/// Construct a trap statement.
/// </summary>
/// <param name="extent">
/// The extent of the trap statement, starting with the trap keyword and ending with the closing curly of the body.
/// </param>
/// <param name="trapType">The type handled by the trap statement, may be null if all exceptions are trapped.</param>
/// <param name="body">The handler for the error.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="body"/> is null.
/// </exception>
public TrapStatementAst ( IScriptExtent extent , TypeConstraintAst trapType , StatementBlockAst body )
: base ( extent )
{
if ( body == null )
{
throw PSTraceSource . NewArgumentNullException ( "body" );
}
if ( trapType != null )
{
this . TrapType = trapType ;
SetParent ( trapType );
}
this . Body = body ;
SetParent ( body );
}
/// <summary>
/// The ast for the type trapped by this trap block, or null if no type was specified.
/// </summary>
public TypeConstraintAst TrapType { get ; private set ; }
/// <summary>
/// The body for the trap block. This property is never null.
/// </summary>
public StatementBlockAst Body { get ; private set ; }
/// <summary>
/// Copy the TrapStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newTrapType = CopyElement ( this . TrapType );
var newBody = CopyElement ( this . Body );
return new TrapStatementAst ( this . Extent , newTrapType , newBody );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Body . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitTrap ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitTrap ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && TrapType != null )
action = TrapType . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
#endregion Exception Handling Statements
#region Flow Control Statements
/// <summary>
/// The ast representing the break statement.
/// </summary>
public class BreakStatementAst : StatementAst
{
/// <summary>
/// Construct a break statement ast.
/// </summary>
/// <param name="extent">The extent of the statement, including the break keyword and the optional label.</param>
/// <param name="label">The optional label expression.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public BreakStatementAst ( IScriptExtent extent , ExpressionAst label )
: base ( extent )
{
if ( label != null )
{
this . Label = label ;
SetParent ( label );
}
}
/// <summary>
/// The expression or label to break to, or null if no label was specified.
/// </summary>
public ExpressionAst Label { get ; private set ; }
/// <summary>
/// Copy the BreakStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newLabel = CopyElement ( this . Label );
return new BreakStatementAst ( this . Extent , newLabel );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitBreakStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitBreakStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && Label != null )
action = Label . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast representing the continue statement.
/// </summary>
public class ContinueStatementAst : StatementAst
{
/// <summary>
/// Construct a continue statement.
/// </summary>
/// <param name="extent">The extent of the statement including the optional label.</param>
/// <param name="label">The optional label expression.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public ContinueStatementAst ( IScriptExtent extent , ExpressionAst label )
: base ( extent )
{
if ( label != null )
{
this . Label = label ;
SetParent ( label );
}
}
/// <summary>
/// The expression or label to continue to, or null if no label was specified.
/// </summary>
public ExpressionAst Label { get ; private set ; }
/// <summary>
/// Copy the ContinueStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newLabel = CopyElement ( this . Label );
return new ContinueStatementAst ( this . Extent , newLabel );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitContinueStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitContinueStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && Label != null )
action = Label . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast representing the return statement.
/// </summary>
public class ReturnStatementAst : StatementAst
{
/// <summary>
/// Construct a return statement.
/// </summary>
/// <param name="extent">The extent of the statement including the optional return value.</param>
/// <param name="pipeline">The optional return value.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public ReturnStatementAst ( IScriptExtent extent , PipelineBaseAst pipeline )
: base ( extent )
{
if ( pipeline != null )
{
this . Pipeline = pipeline ;
SetParent ( pipeline );
}
}
/// <summary>
/// The pipeline specified in the return statement, or null if none was specified.
/// </summary>
public PipelineBaseAst Pipeline { get ; private set ; }
/// <summary>
/// Copy the ReturnStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newPipeline = CopyElement ( this . Pipeline );
return new ReturnStatementAst ( this . Extent , newPipeline );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitReturnStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitReturnStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && Pipeline != null )
action = Pipeline . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast representing the exit statement.
/// </summary>
public class ExitStatementAst : StatementAst
{
/// <summary>
/// Construct an exit statement.
/// </summary>
/// <param name="extent">The extent of the exit statement including the optional exit value.</param>
/// <param name="pipeline">The optional exit value.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public ExitStatementAst ( IScriptExtent extent , PipelineBaseAst pipeline )
: base ( extent )
{
if ( pipeline != null )
{
this . Pipeline = pipeline ;
SetParent ( pipeline );
}
}
/// <summary>
/// The pipeline specified in the exit statement, or null if none was specified.
/// </summary>
public PipelineBaseAst Pipeline { get ; private set ; }
/// <summary>
/// Copy the ExitStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newPipeline = CopyElement ( this . Pipeline );
return new ExitStatementAst ( this . Extent , newPipeline );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitExitStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitExitStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && Pipeline != null )
action = Pipeline . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast representing the throw statement.
/// </summary>
public class ThrowStatementAst : StatementAst
{
/// <summary>
/// Construct a throw statement.
/// </summary>
/// <param name="extent">The extent of the throw statement, including the optional value to throw.</param>
/// <param name="pipeline">The optional value to throw.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public ThrowStatementAst ( IScriptExtent extent , PipelineBaseAst pipeline )
: base ( extent )
{
if ( pipeline != null )
{
this . Pipeline = pipeline ;
SetParent ( pipeline );
}
}
/// <summary>
/// The pipeline specified in the throw statement, or null if none was specified.
/// </summary>
public PipelineBaseAst Pipeline { get ; private set ; }
/// <summary>
/// If the throw statement is a rethrow. In PowerShell, a throw statement need not throw anything. Such
/// a throw statement throws a new exception if it does not appear lexically withing a catch, otherwise
/// it rethrows the caught exception. Examples:
/// <c>
/// if ($true) { throw } # not a rethrow
/// try { foo } catch { throw } # rethrow
/// try { foo } catch { . { throw } } # rethrow
/// try { foo } catch { function foo { throw } } # rethrow
/// try { foo } finally { throw } # not a rethrow
/// </c>
/// </summary>
public bool IsRethrow
{
get
{
if ( Pipeline != null )
return false ;
var parent = Parent ;
while ( parent != null )
{
if ( parent is CatchClauseAst )
{
return true ;
}
parent = parent . Parent ;
}
return false ;
}
}
/// <summary>
/// Copy the ThrowStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newPipeline = CopyElement ( this . Pipeline );
return new ThrowStatementAst ( this . Extent , newPipeline );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitThrowStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitThrowStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && Pipeline != null )
action = Pipeline . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
#endregion Flow Control Statements
#region Pipelines
/// <summary>
/// An abstract base class for statements that include command invocations, pipelines, expressions, and assignements.
/// Any statement that does not begin with a keyword is derives from PipelineBastAst.
/// </summary>
public abstract class PipelineBaseAst : StatementAst
{
/// <summary>
/// Initialize the common parts of a PipelineBaseAst.
/// </summary>
/// <param name="extent">The extent of the statement.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
protected PipelineBaseAst ( IScriptExtent extent )
: base ( extent )
{
}
/// <summary>
/// If the pipeline represents a pure expression, the expression is returned, otherwise null is returned.
/// </summary>
public virtual ExpressionAst GetPureExpression ()
{
return null ;
}
}
/// <summary>
/// The ast that repesents a PowerShell pipeline, e.g. <c>gci -re . *.cs | select-string Foo</c> or <c> 65..90 | % { [char]$_ }</c>.
/// A pipeline must have at least 1 command. The first command may be an expression or a command invocation.
/// </summary>
public class PipelineAst : PipelineBaseAst
{
/// <summary>
/// Construct a pipeline from a collection of commands.
/// </summary>
/// <param name="extent">The extent of the pipeline.</param>
/// <param name="pipelineElements">The collection of commands representing the pipeline.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="pipelineElements"/> is null or is an empty collection.
/// </exception>
public PipelineAst ( IScriptExtent extent , IEnumerable < CommandBaseAst > pipelineElements )
: base ( extent )
{
if ( pipelineElements == null || ! pipelineElements . Any ())
{
throw PSTraceSource . NewArgumentException ( "pipelineElements" );
}
this . PipelineElements = new ReadOnlyCollection < CommandBaseAst >( pipelineElements . ToArray ());
SetParents ( PipelineElements );
}
/// <summary>
/// Construct a pipeline from a single command.
/// </summary>
/// <param name="extent">The extent of the pipeline (which should be the extent of the command).</param>
/// <param name="commandAst">The command for the pipeline.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="commandAst"/> is null.
/// </exception>
public PipelineAst ( IScriptExtent extent , CommandBaseAst commandAst )
: base ( extent )
{
if ( commandAst == null )
{
throw PSTraceSource . NewArgumentNullException ( "commandAst" );
}
this . PipelineElements = new ReadOnlyCollection < CommandBaseAst >( new CommandBaseAst [] { commandAst });
SetParent ( commandAst );
}
/// <summary>
/// A non-null, non-empty collection of commands that represent the pipeline.
/// </summary>
public ReadOnlyCollection < CommandBaseAst > PipelineElements { get ; private set ; }
/// <summary>
/// If the pipeline represents a pure expression, the expression is returned, otherwise null is returned.
/// </summary>
public override ExpressionAst GetPureExpression ()
{
if ( PipelineElements . Count != 1 )
{
return null ;
}
CommandExpressionAst expr = PipelineElements [ 0 ] as CommandExpressionAst ;
if ( expr != null && ! expr . Redirections . Any ())
{
return expr . Expression ;
}
return null ;
}
/// <summary>
/// Copy the PipelineAst instance
/// </summary>
public override Ast Copy ()
{
var newPipelineElements = CopyElements ( this . PipelineElements );
return new PipelineAst ( this . Extent , newPipelineElements );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return PipelineElements . Last (). GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitPipeline ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitPipeline ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < PipelineElements . Count ; index ++)
{
var commandAst = PipelineElements [ index ];
action = commandAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// An abstract base class for the components of a <see cref="CommandAst"/>.
/// </summary>
public abstract class CommandElementAst : Ast
{
/// <summary>
/// Initialize the common fields of a comment element.
/// </summary>
/// <param name="extent">The extent of the command element.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
protected CommandElementAst ( IScriptExtent extent )
: base ( extent )
{
}
}
/// <summary>
/// The ast that represents a parameter to a command, e.g. <c>dir -Path a*</c>, this class represent '-Path', and
/// in <c>dir -Path:a*</c>, this class represents '-Path:a*'.
///
/// In the first case, the argument 'a*' is not represented by this class because the parser can't know until runtime
/// if the argument is positional or if -Path accepts an argument. In the later case, the argument 'a*' always
/// belongs to the parameter -Path.
/// </summary>
public class CommandParameterAst : CommandElementAst
{
/// <summary>
/// Construct a command parameter.
/// </summary>
/// <param name="extent">
/// The extent of the parameter, starting from the dash character, ending at the end of the parameter name, or else
/// at the end of the optional argument.
/// </param>
/// <param name="parameterName">
/// The parameter name, without the leading dash and without the trailing colon, if a colon was used.
/// </param>
/// <param name="argument">
/// If the parameter includes an argument with the syntax like <c>-Path:a*</c>, then the expression for 'a*' is
/// passed as the argument. An argument is not required.
/// </param>
/// <param name="errorPosition">
/// The extent to use for error reporting when parameter binding fails with this parameter. If <paramref name="argument"/>
/// is null, this extent is the same as <paramref name="extent"/>, otherwise it is the extent of the parameter token
/// itself.
/// </param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="parameterName"/>is null, or if <paramref name="parameterName"/>
/// is an empty string.
/// </exception>
public CommandParameterAst ( IScriptExtent extent , string parameterName , ExpressionAst argument , IScriptExtent errorPosition )
: base ( extent )
{
if ( errorPosition == null || string . IsNullOrEmpty ( parameterName ))
{
throw PSTraceSource . NewArgumentNullException ( errorPosition == null ? "errorPosition" : "parameterName" );
}
this . ParameterName = parameterName ;
if ( argument != null )
{
this . Argument = argument ;
SetParent ( argument );
}
this . ErrorPosition = errorPosition ;
}
/// <summary>
/// The name of the parameter. This value does not include a leading dash, and in the case that an argument
/// is specified, no trailing colon is included either. This property is never null or empty.
/// </summary>
public string ParameterName { get ; private set ; }
/// <summary>
/// The ast for the argument if specified (e.g. -Path:-abc, then the argument is the ast for '-ast'), otherwise null
/// if no argument was specified.
/// </summary>
public ExpressionAst Argument { get ; private set ; }
/// <summary>
/// The error position to use when parameter binding fails. This extent does not include the argument if one was
/// specified, which means this extent is often the same as <see cref="Ast.Extent"/>.
/// </summary>
public IScriptExtent ErrorPosition { get ; private set ; }
/// <summary>
/// Copy the CommandParameterAst instance
/// </summary>
public override Ast Copy ()
{
var newArgument = CopyElement ( this . Argument );
return new CommandParameterAst ( this . Extent , this . ParameterName , newArgument , this . ErrorPosition );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitCommandParameter ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitCommandParameter ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( Argument != null && action == AstVisitAction . Continue )
{
action = Argument . InternalVisit ( visitor );
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// An abstract base class for a command and an expression wrapper that allows an expression as a command in a pipeline.
/// </summary>
public abstract class CommandBaseAst : StatementAst
{
private static readonly ReadOnlyCollection < RedirectionAst > EmptyRedirections =
Utils . EmptyReadOnlyCollection < RedirectionAst >();
internal const int MaxRedirections = ( int ) RedirectionStream . Information + 1 ;
/// <summary>
/// Initialize the common fields of a command.
/// </summary>
/// <param name="extent">The extent of the command.</param>
/// <param name="redirections">The redirections for the command, may be null.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
protected CommandBaseAst ( IScriptExtent extent , IEnumerable < RedirectionAst > redirections )
: base ( extent )
{
if ( redirections != null )
{
this . Redirections = new ReadOnlyCollection < RedirectionAst >( redirections . ToArray ());
SetParents ( Redirections );
}
else
{
this . Redirections = EmptyRedirections ;
}
}
/// <summary>
/// The possibly empty collection of redirections for this command.
/// </summary>
public ReadOnlyCollection < RedirectionAst > Redirections { get ; private set ; }
}
/// <summary>
/// The ast for a command invocation, e.g. <c>dir *.ps1</c>.
/// </summary>
public class CommandAst : CommandBaseAst
{
/// <summary>
/// Construct a command invocation.
/// </summary>
/// <param name="extent">
/// The extent of the command, starting with either the optional invocation operator '&' or '.' or the command name
/// and ending with the last command element.
/// </param>
/// <param name="commandElements">The elements of the command (command name, parameters and expressions.)</param>
/// <param name="invocationOperator">The invocation operator that was used, if any.</param>
/// <param name="redirections">The redirections for the command, may be null.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="commandElements"/> is null or is an empty collection.
/// </exception>
public CommandAst ( IScriptExtent extent ,
IEnumerable < CommandElementAst > commandElements ,
TokenKind invocationOperator ,
IEnumerable < RedirectionAst > redirections )
: base ( extent , redirections )
{
if ( commandElements == null || ! commandElements . Any ())
{
throw PSTraceSource . NewArgumentException ( "commandElements" );
}
if ( invocationOperator != TokenKind . Dot && invocationOperator != TokenKind . Ampersand && invocationOperator != TokenKind . Unknown )
{
throw PSTraceSource . NewArgumentException ( "invocationOperator" );
}
this . CommandElements = new ReadOnlyCollection < CommandElementAst >( commandElements . ToArray ());
SetParents ( CommandElements );
this . InvocationOperator = invocationOperator ;
}
/// <summary>
/// A non-empty collection of command elements. This property is never null.
/// </summary>
public ReadOnlyCollection < CommandElementAst > CommandElements { get ; private set ; }
/// <summary>
/// The invocation operator (either <see cref="TokenKind.Dot"/> or <see cref="TokenKind.Ampersand"/>) if one was specified,
/// otherwise the value is <see cref="TokenKind.Unknown"/>.
/// </summary>
public TokenKind InvocationOperator { get ; private set ; }
/// <summary>
/// <para>Returns the name of the command invoked by this ast.</para>
///
/// <para>This command name may not be known statically, in which case null is returned.</para>
///
/// <para>
/// For example, if the command name is in a variable: <example>& $foo</example>, then the parser cannot know which command is executed.
/// Similarly, if the command is being invoked in a module: <example>& (gmo SomeModule) Bar</example>, then the parser does not know the
/// command name is Bar because the parser can't determine that the expression <code>(gmo SomeModule)</code> returns a module instead
/// of a string.
/// </para>
/// </summary>
/// <returns>The command name, if known, null otherwise.</returns>
public string GetCommandName ()
{
var name = CommandElements [ 0 ] as StringConstantExpressionAst ;
return name != null ? name . Value : null ;
}
/// <summary>
/// If this command was synthesiszed out of a dynamic keyword, this property will point to the DynamicKeyword
/// data structure that was used to create this command.
/// </summary>
public DynamicKeyword DefiningKeyword { get ; set ; }
/// <summary>
/// Copy the CommandAst instance
/// </summary>
public override Ast Copy ()
{
var newCommandElements = CopyElements ( this . CommandElements );
var newRedirections = CopyElements ( this . Redirections );
return new CommandAst ( this . Extent , newCommandElements , this . InvocationOperator , newRedirections )
{
DefiningKeyword = this . DefiningKeyword
};
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
PseudoBindingInfo pseudoBinding = new PseudoParameterBinder ()
. DoPseudoParameterBinding ( this , null , null , PseudoParameterBinder . BindingType . ParameterCompletion );
if ( pseudoBinding == null || pseudoBinding . CommandInfo == null )
{
yield break ;
}
AstParameterArgumentPair pathArgument ;
string pathParameterName = "Path" ;
if (! pseudoBinding . BoundArguments . TryGetValue ( pathParameterName , out pathArgument ))
{
pathParameterName = "LiteralPath" ;
pseudoBinding . BoundArguments . TryGetValue ( pathParameterName , out pathArgument );
}
var commandInfo = pseudoBinding . CommandInfo ;
var pathArgumentPair = pathArgument as AstPair ;
if ( pathArgumentPair != null && pathArgumentPair . Argument is StringConstantExpressionAst )
{
var pathValue = (( StringConstantExpressionAst ) pathArgumentPair . Argument ). Value ;
try
{
commandInfo = commandInfo . CreateGetCommandCopy ( new [] { "-" + pathParameterName , pathValue });
}
catch ( InvalidOperationException ) { }
}
var cmdletInfo = commandInfo as CmdletInfo ;
if ( cmdletInfo != null )
{
// Special cases
// new-object - yields an instance of whatever -Type is bound to
if ( cmdletInfo . ImplementingType . FullName . Equals ( "Microsoft.PowerShell.Commands.NewObjectCommand" , StringComparison . Ordinal ))
{
AstParameterArgumentPair typeArgument ;
if ( pseudoBinding . BoundArguments . TryGetValue ( "TypeName" , out typeArgument ))
{
var typeArgumentPair = typeArgument as AstPair ;
if ( typeArgumentPair != null && typeArgumentPair . Argument is StringConstantExpressionAst )
{
yield return new PSTypeName ((( StringConstantExpressionAst ) typeArgumentPair . Argument ). Value );
}
}
yield break ;
}
// Get-CimInstance/New-CimInstance - yields a CimInstance with ETS type based on its arguments for -Namespace and -ClassName parameters
if ( cmdletInfo . ImplementingType . FullName . Equals ( "Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand" , StringComparison . Ordinal ) ||
cmdletInfo . ImplementingType . FullName . Equals ( "Microsoft.Management.Infrastructure.CimCmdlets.NewCimInstanceCommand" , StringComparison . Ordinal ))
{
string pseudoboundNamespace = CompletionCompleters . NativeCommandArgumentCompletion_ExtractSecondaryArgument ( pseudoBinding . BoundArguments , "Namespace" ). FirstOrDefault ();
string pseudoboundClassName = CompletionCompleters . NativeCommandArgumentCompletion_ExtractSecondaryArgument ( pseudoBinding . BoundArguments , "ClassName" ). FirstOrDefault ();
if (! string . IsNullOrWhiteSpace ( pseudoboundClassName ))
{
yield return new PSTypeName ( string . Format (
CultureInfo . InvariantCulture ,
"{0}#{1}/{2}" ,
typeof ( Microsoft . Management . Infrastructure . CimInstance ). FullName ,
pseudoboundNamespace ?? "root/cimv2" ,
pseudoboundClassName ));
}
yield return new PSTypeName ( typeof ( Microsoft . Management . Infrastructure . CimInstance ));
yield break ;
}
// where-object - yields whatever we saw before where-object in the pipeline.
// same for sort-object
if ( cmdletInfo . ImplementingType == typeof ( WhereObjectCommand )
|| cmdletInfo . ImplementingType . FullName . Equals ( "Microsoft.PowerShell.Commands.SortObjectCommand" , StringComparison . Ordinal ))
{
var parentPipeline = this . Parent as PipelineAst ;
if ( parentPipeline != null )
{
int i ;
for ( i = 0 ; i < parentPipeline . PipelineElements . Count ; i ++)
{
if ( parentPipeline . PipelineElements [ i ] == this )
break ;
}
if ( i > 0 )
{
foreach ( var typename in parentPipeline . PipelineElements [ i - 1 ]. GetInferredType ( context ))
{
yield return typename ;
}
}
}
// We could also check -InputObject, but that is rarely used. But don't bother continuing.
yield break ;
}
// foreach-object - yields the type of it's script block parameters
if ( cmdletInfo . ImplementingType == typeof ( ForEachObjectCommand ))
{
AstParameterArgumentPair argument ;
if ( pseudoBinding . BoundArguments . TryGetValue ( "Begin" , out argument ))
{
foreach ( var type in GetInferredTypeFromScriptBlockParameter ( argument , context ))
{
yield return type ;
}
}
if ( pseudoBinding . BoundArguments . TryGetValue ( "Process" , out argument ))
{
foreach ( var type in GetInferredTypeFromScriptBlockParameter ( argument , context ))
{
yield return type ;
}
}
if ( pseudoBinding . BoundArguments . TryGetValue ( "End" , out argument ))
{
foreach ( var type in GetInferredTypeFromScriptBlockParameter ( argument , context ))
{
yield return type ;
}
}
}
}
// The OutputType property ignores the parameter set specified in the OutputTypeAttribute.
// With psuedo-binding, we actually know the candidate parameter sets, so we could take
// advantage of it here, but I opted for the simpler code because so few cmdlets use
// ParameterSetName in OutputType and of the ones I know about, it isn't that useful.
foreach ( var result in commandInfo . OutputType )
{
yield return result ;
}
}
private IEnumerable < PSTypeName > GetInferredTypeFromScriptBlockParameter ( AstParameterArgumentPair argument , CompletionContext context )
{
var argumentPair = argument as AstPair ;
if ( argumentPair != null && argumentPair . Argument is ScriptBlockExpressionAst )
{
var scriptBlockExpressionAst = ( ScriptBlockExpressionAst ) argumentPair . Argument ;
foreach ( var type in scriptBlockExpressionAst . ScriptBlock . GetInferredType ( context ))
{
yield return type ;
}
}
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitCommand ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitCommand ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < CommandElements . Count ; index ++)
{
var commandElementAst = CommandElements [ index ];
action = commandElementAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Redirections . Count ; index ++)
{
var redirection = Redirections [ index ];
if ( action == AstVisitAction . Continue )
{
action = redirection . InternalVisit ( visitor );
}
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast representing an expression when the expression is used as the first command of a pipeline.
/// </summary>
public class CommandExpressionAst : CommandBaseAst
{
/// <summary>
/// Construct a command that wraps an expression.
/// </summary>
/// <param name="extent">The extent of the expression.</param>
/// <param name="expression">The expression being wrapped.</param>
/// <param name="redirections">The redirections for the command, may be null.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="expression"/> is null.
/// </exception>
public CommandExpressionAst ( IScriptExtent extent ,
ExpressionAst expression ,
IEnumerable < RedirectionAst > redirections )
: base ( extent , redirections )
{
if ( expression == null )
{
throw PSTraceSource . NewArgumentNullException ( "expression" );
}
this . Expression = expression ;
SetParent ( expression );
}
/// <summary>
/// The ast for the expression that is or starts a pipeline. This property is never null.
/// </summary>
public ExpressionAst Expression { get ; private set ; }
/// <summary>
/// Copy the CommandExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newExpression = CopyElement ( this . Expression );
var newRedirections = CopyElements ( this . Redirections );
return new CommandExpressionAst ( this . Extent , newExpression , newRedirections );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Expression . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitCommandExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitCommandExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Expression . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Redirections . Count ; index ++)
{
var redirection = Redirections [ index ];
if ( action == AstVisitAction . Continue )
{
action = redirection . InternalVisit ( visitor );
}
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// An abstract base class representing both file redirections and merging redirections.
/// </summary>
public abstract class RedirectionAst : Ast
{
/// <summary>
/// Initialize the common fields in a redirection.
/// </summary>
/// <param name="extent">The extent of the redirection.</param>
/// <param name="from">The stream to read from.</param>
protected RedirectionAst ( IScriptExtent extent , RedirectionStream from )
: base ( extent )
{
this . FromStream = from ;
}
/// <summary>
/// The stream to read objects from. Objects are either merged with another stream, or written to a file.
/// </summary>
public RedirectionStream FromStream { get ; private set ; }
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return EmptyPSTypeNameArray ;
}
}
/// <summary>
/// The stream number that is redirected.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
public enum RedirectionStream
{
/// <summary>
/// All streams, used when the redirection token uses '*' as the stream number.
/// </summary>
All = 0 ,
/// <summary>
/// The normal output stream.
/// </summary>
Output = 1 ,
/// <summary>
/// The error stream.
/// </summary>
Error = 2 ,
/// <summary>
/// The warning stream.
/// </summary>
Warning = 3 ,
/// <summary>
/// The verbose stream.
/// </summary>
Verbose = 4 ,
/// <summary>
/// The debug stream.
/// </summary>
Debug = 5 ,
/// <summary>
/// The information stream.
/// </summary>
Information = 6
}
/// <summary>
/// The ast representing a redirection that merges 2 streams, e.g. <c>dir 2>&1</c>
/// </summary>
public class MergingRedirectionAst : RedirectionAst
{
/// <summary>
/// Construct a merging redirection.
/// </summary>
/// <param name="extent">The extent of the redirection.</param>
/// <param name="from">The stream to read from.</param>
/// <param name="to">The stream to write to - must always be <see cref="RedirectionStream.Output"/></param>
/// <exception cref="PSArgumentNullException">If <paramref name="extent"/> is null.</exception>
public MergingRedirectionAst ( IScriptExtent extent , RedirectionStream from , RedirectionStream to )
: base ( extent , from )
{
this . ToStream = to ;
}
/// <summary>
/// The stream that results will be written to.
/// </summary>
public RedirectionStream ToStream { get ; private set ; }
/// <summary>
/// Copy the MergingRedirectionAst instance
/// </summary>
public override Ast Copy ()
{
return new MergingRedirectionAst ( this . Extent , this . FromStream , this . ToStream );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitMergingRedirection ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitMergingRedirection ( this );
return visitor . CheckForPostAction ( this , action == AstVisitAction . SkipChildren ? AstVisitAction . Continue : action );
}
#endregion Visitors
}
/// <summary>
/// The ast representing a redirection to a file, e.g. <c>dir > out.txt</c>, the '> out.txt' is represented by this ast.
/// </summary>
public class FileRedirectionAst : RedirectionAst
{
/// <summary>
/// Construct a redirection to a file.
/// </summary>
/// <param name="extent">
/// The extent of the redirection, starting with the redirection operator and including the file.
/// </param>
/// <param name="stream">
/// The stream being redirected.
/// </param>
/// <param name="file">
/// The optional location to redirect to. Merging operators may not specify a file, the other redirection
/// operators must specify a location.
/// </param>
/// <param name="append">
/// True if the file is being appended, false otherwise.
/// </param>
/// <exception cref="PSArgumentNullException">If <paramref name="extent"/> is null.</exception>
/// <exception cref="PSArgumentException">If <paramref name="file"/> is null.</exception>
public FileRedirectionAst ( IScriptExtent extent , RedirectionStream stream , ExpressionAst file , bool append )
: base ( extent , stream )
{
if ( file == null )
{
throw PSTraceSource . NewArgumentNullException ( "file" );
}
this . Location = file ;
SetParent ( file );
this . Append = append ;
}
/// <summary>
/// The ast for the location to redirect to.
/// </summary>
public ExpressionAst Location { get ; private set ; }
/// <summary>
/// True if the file is appended, false otherwise.
/// </summary>
public bool Append { get ; private set ; }
/// <summary>
/// Copy the FileRedirectionAst instance
/// </summary>
public override Ast Copy ()
{
var newFile = CopyElement ( this . Location );
return new FileRedirectionAst ( this . Extent , this . FromStream , newFile , this . Append );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitFileRedirection ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitFileRedirection ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Location . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
#endregion Pipelines
/// <summary>
/// The ast that represents an assignment statement, e.g. <c>$x = 42</c>.
/// </summary>
public class AssignmentStatementAst : PipelineBaseAst
{
/// <summary>
/// Construct an assignement statement.
/// </summary>
/// <param name="extent">The extent of the assignment statement.</param>
/// <param name="left">The value being assigned.</param>
/// <param name="operator">The assignment operator, e.g. '=' or '+='.</param>
/// <param name="right">The value to assign.</param>
/// <param name="errorPosition">The position to report an error if an error occurs at runtime.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="left"/>, <paramref name="right"/>,
/// or <paramref name="errorPosition"/> is null.
/// </exception>
public AssignmentStatementAst ( IScriptExtent extent , ExpressionAst left , TokenKind @operator , StatementAst right , IScriptExtent errorPosition )
: base ( extent )
{
if ( left == null || right == null || errorPosition == null )
{
throw PSTraceSource . NewArgumentNullException ( left == null ? "left" : right == null ? "right" : "errorPosition" );
}
if (( @operator . GetTraits () & TokenFlags . AssignmentOperator ) == 0 )
{
throw PSTraceSource . NewArgumentException ( "operator" );
}
// If the assignment is just an expression - remove the pipeline wrapping the expression.
var pipelineAst = right as PipelineAst ;
if ( pipelineAst != null )
{
if ( pipelineAst . PipelineElements . Count == 1 )
{
var commandExpressionAst = pipelineAst . PipelineElements [ 0 ] as CommandExpressionAst ;
if ( commandExpressionAst != null )
{
right = commandExpressionAst ;
right . ClearParent ();
}
}
}
this . Operator = @operator ;
this . Left = left ;
SetParent ( left );
this . Right = right ;
SetParent ( right );
this . ErrorPosition = errorPosition ;
}
/// <summary>
/// The ast for the location being assigned. This property is never null.
/// </summary>
public ExpressionAst Left { get ; private set ; }
/// <summary>
/// The operator for token assignment (such as =, +=, -=, etc.). The value is always some assignment operator.
/// </summary>
public TokenKind Operator { get ; private set ; }
/// <summary>
/// The ast for the value to assign. This property is never null.
/// </summary>
public StatementAst Right { get ; private set ; }
/// <summary>
/// The position to report at runtime if there is an error during assignment. This property is never null.
/// </summary>
public IScriptExtent ErrorPosition { get ; private set ; }
/// <summary>
/// Copy the AssignmentStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newLeft = CopyElement ( this . Left );
var newRight = CopyElement ( this . Right );
return new AssignmentStatementAst ( this . Extent , newLeft , this . Operator , newRight , this . ErrorPosition );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Left . GetInferredType ( context );
}
/// <summary>
/// Return all of the expressions assigned by the assignment statement. Typically
/// it's just a variable expression, but if <see cref="Left"/> is an <see cref="ArrayLiteralAst"/>,
/// then all of the elements are assigned.
/// </summary>
/// <returns>All of the expressions assigned by the assignment statement.</returns>
public IEnumerable < ExpressionAst > GetAssignmentTargets ()
{
var arrayExpression = Left as ArrayLiteralAst ;
if ( arrayExpression != null )
{
foreach ( var element in arrayExpression . Elements )
{
yield return element ;
}
yield break ;
}
yield return Left ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitAssignmentStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitAssignmentStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Left . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Right . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// Defines types of configuration document
/// </summary>
public enum ConfigurationType
{
/// <summary>
/// Resource configuration
/// </summary>
Resource = 0 ,
/// <summary>
/// Meta configuration
/// </summary>
Meta = 1
}
/// <summary>
/// The ast represents the DSC configuration statement
/// </summary>
public class ConfigurationDefinitionAst : StatementAst
{
/// <summary>
/// Construct a configuration statement
/// </summary>
/// <param name="extent">
/// The extent of the expression, starting with the attribute and ending after the expression being attributed.
/// </param>
/// <param name="body"><see cref="ScriptBlockExpressionAst"/> of the configuration statement</param>
/// <param name="type">The type of the configuration</param>
/// <param name="instanceName">The configuration name expression</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="body"/>, or <paramref name="instanceName"/> is null.
/// </exception>
public ConfigurationDefinitionAst ( IScriptExtent extent ,
ScriptBlockExpressionAst body ,
ConfigurationType type ,
ExpressionAst instanceName ) : base ( extent )
{
if ( extent == null )
{
throw PSTraceSource . NewArgumentNullException ( "extent" );
}
if ( body == null )
{
throw PSTraceSource . NewArgumentNullException ( "body" );
}
if ( instanceName == null )
{
throw PSTraceSource . NewArgumentNullException ( "instanceName" );
}
this . Body = body ;
SetParent ( body );
this . ConfigurationType = type ;
this . InstanceName = instanceName ;
SetParent ( instanceName );
}
/// <summary>
/// This ast represents configuration body script block.
/// This property is never null.
/// </summary>
public ScriptBlockExpressionAst Body { get ; private set ; }
/// <summary>
/// The configuration type
/// </summary>
public ConfigurationType ConfigurationType { get ; private set ; }
/// <summary>
/// The name of the configuration instance,
/// For example, Instance name of 'configuration test { ...... }' is 'test'
/// This property is never null.
/// </summary>
public ExpressionAst InstanceName { get ; private set ; }
/// <summary>
/// Duplicates the <see cref="ConfigurationDefinitionAst"/>, allowing it to be composed into other ASTs.
/// </summary>
/// <returns>A copy of the <see cref="ConfigurationDefinitionAst"/>, with the link to the previous parent removed.</returns>
public override Ast Copy ()
{
ScriptBlockExpressionAst body = CopyElement ( Body );
ExpressionAst instanceName = CopyElement ( InstanceName );
return new ConfigurationDefinitionAst ( Extent , body , ConfigurationType , instanceName )
{
LCurlyToken = this . LCurlyToken ,
ConfigurationToken = this . ConfigurationToken ,
CustomAttributes = this . CustomAttributes == null ? null : this . CustomAttributes . Select ( e => ( AttributeAst ) e . Copy ())
};
}
/// <summary>
/// Gets inferred type
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Body . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
var visitor2 = visitor as ICustomAstVisitor2 ;
return visitor2 != null ? visitor2 . VisitConfigurationDefinition ( this ) : null ;
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = AstVisitAction . Continue ;
var visitor2 = visitor as AstVisitor2 ;
if ( visitor2 != null )
{
action = visitor2 . VisitConfigurationDefinition ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
}
if ( action == AstVisitAction . Continue )
{
action = InstanceName . InternalVisit ( visitor );
}
if ( action == AstVisitAction . Continue )
{
Body . InternalVisit ( visitor );
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
#region Internal methods / properties
internal Token LCurlyToken { get ; set ; }
internal Token ConfigurationToken { get ; set ; }
internal IEnumerable < AttributeAst > CustomAttributes { get ; set ; }
/// <summary>
/// A dynamic keyword may also define additional keywords in the child scope
/// of the scriptblock. This collection will contain those keywords.
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
internal List < DynamicKeyword > DefinedKeywords { get ; set ; }
/// <summary>
/// Generate ast that definies a function for this <see cref="ConfigurationDefinitionAst"/> object
/// </summary>
/// <returns>
/// The <see cref="PipelineAst"/> that defines a function for this <see cref="ConfigurationDefinitionAst"/> object
/// </returns>
internal PipelineAst GenerateSetItemPipelineAst ()
{
//**************************
// Now construct the AST to call the function that will build the actual object.
// This is composed of a call to command with the signature
// function PSDesiredStateConfiguration\\Configuration
// {
// param (
// ResourceModuleTuplesToImport, # list of required resource-module tuples
// $OutputPath = ".", # output path where the MOF will be placed
// $Name, # name of the configuration == name of the wrapper function
// [scriptblock]
// $Body, # the body of the configuration
// [hashtable]
// $ArgsToBody, # the argument values to pass to the body scriptblock
// [hashtable]
// $ConfigurationData, # a collection of property bags describe the configuration environment
// [string]
// $InstanceName = "" # THe name of the configuration instance being created.
// [boolean]
// $IsMetaConfig = $false # the configuration to generated is a meta configuration
// )
// }
var cea = new Collection < CommandElementAst >
{
new StringConstantExpressionAst ( this . Extent ,
@"PSDesiredStateConfiguration\Configuration" ,
StringConstantType . BareWord ),
new CommandParameterAst ( LCurlyToken . Extent , "ArgsToBody" , new VariableExpressionAst ( LCurlyToken . Extent , "toBody" , false ), LCurlyToken . Extent ),
new CommandParameterAst ( LCurlyToken . Extent , "Name" , ( ExpressionAst ) InstanceName . Copy (), LCurlyToken . Extent )
};
///////////////////////////
// get import parameters
var bodyStatements = Body . ScriptBlock . EndBlock . Statements ;
var resourceModulePairsToImport = new List < Tuple < string [], ModuleSpecification [], Version >>();
var resourceBody = ( from stm in bodyStatements where ! IsImportCommand ( stm , resourceModulePairsToImport ) select ( StatementAst ) stm . Copy ()). ToList ();
cea . Add ( new CommandParameterAst ( PositionUtilities . EmptyExtent , "ResourceModuleTuplesToImport" , new ConstantExpressionAst ( PositionUtilities . EmptyExtent , resourceModulePairsToImport ), PositionUtilities . EmptyExtent ));
var scriptBlockBody = new ScriptBlockAst ( Body . Extent ,
CustomAttributes == null ? null : CustomAttributes . Select ( att => ( AttributeAst ) att . Copy ()). ToList (),
null ,
new StatementBlockAst ( Body . Extent , resourceBody , null ),
false , false );
var scriptBlockExp = new ScriptBlockExpressionAst ( Body . Extent , scriptBlockBody );
// then the configuration scriptblock as -Body
cea . Add ( new CommandParameterAst ( LCurlyToken . Extent , "Body" , scriptBlockExp , LCurlyToken . Extent ));
cea . Add ( new CommandParameterAst ( LCurlyToken . Extent , "Outputpath" , new VariableExpressionAst ( LCurlyToken . Extent , "OutputPath" , false ), LCurlyToken . Extent ));
cea . Add ( new CommandParameterAst ( LCurlyToken . Extent , "ConfigurationData" , new VariableExpressionAst ( LCurlyToken . Extent , "ConfigurationData" , false ), LCurlyToken . Extent ));
cea . Add ( new CommandParameterAst ( LCurlyToken . Extent , "InstanceName" , new VariableExpressionAst ( LCurlyToken . Extent , "InstanceName" , false ), LCurlyToken . Extent ));
//
// copy the configuration paramter to the new function parameter
// the new set-item created function will have below parameters
// [cmdletbinding()]
// param(
// [string]
// $InstanceName,
// [string[]]
// $DependsOn,
// [string]
// $OutputPath,
// [hashtable]
// [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformation()]
// $ConfigurationData
// )
//
var attribAsts =
ConfigurationBuildInParameterAttribAsts . Select ( attribAst => ( AttributeAst ) attribAst . Copy ()). ToList ();
var paramAsts = ConfigurationBuildInParameters . Select ( paramAst => ( ParameterAst ) paramAst . Copy ()). ToList ();
// the parameters defined in the configuration keyword will be combined with above parameters
// it will be used to construct $ArgsToBody in the set-item created function boby using below statement
// $toBody = @{}+$PSBoundParameters
// $toBody.Remove(""OutputPath"")
// $toBody.Remove(""ConfigurationData"")
// $ConfigurationData = $psboundparameters[""ConfigurationData""]
// $Outputpath = $psboundparameters[""Outputpath""]
if ( Body . ScriptBlock . ParamBlock != null )
{
paramAsts . AddRange ( Body . ScriptBlock . ParamBlock . Parameters . Select ( parameterAst => ( ParameterAst ) parameterAst . Copy ()));
}
var paramBlockAst = new ParamBlockAst ( this . Extent , attribAsts , paramAsts );
var cmdAst = new CommandAst ( this . Extent , cea , TokenKind . Unknown , null );
var pipeLineAst = new PipelineAst ( this . Extent , cmdAst );
var funcStatements = ConfigurationExtraParameterStatements . Select ( statement => ( StatementAst ) statement . Copy ()). ToList ();
funcStatements . Add ( pipeLineAst );
var statmentBlockAst = new StatementBlockAst ( this . Extent , funcStatements , null );
var funcBody = new ScriptBlockAst ( Body . Extent ,
CustomAttributes == null ? null : CustomAttributes . Select ( att => ( AttributeAst ) att . Copy ()). ToList (),
paramBlockAst , statmentBlockAst , false , true );
var funcBodyExp = new ScriptBlockExpressionAst ( this . Extent , funcBody );
#region "Construct Set-Item pipeline"
//**************************
// Now construct the AST to call the set-item cmdlet that will create the function
// it will do set-item -Path function:\ConfigurationNameExpr -Value funcBody
// create function:\confignameexpression
var funcDriveStrExpr = new StringConstantExpressionAst ( this . Extent , @"function:\" ,
StringConstantType . BareWord );
var funcPathStrExpr = new BinaryExpressionAst ( this . Extent , funcDriveStrExpr ,
TokenKind . Plus ,
( ExpressionAst ) InstanceName . Copy (),
this . Extent );
var setItemCmdElements = new Collection < CommandElementAst >
{
new StringConstantExpressionAst ( this . Extent , @"set-item" ,
StringConstantType . BareWord ),
new CommandParameterAst ( this . Extent , "Path" ,
funcPathStrExpr ,
this . Extent ),
new CommandParameterAst ( this . Extent , "Value" , funcBodyExp ,
this . Extent )
};
// then the configuration scriptblock as function body
var setItemCmdlet = new CommandAst ( this . Extent , setItemCmdElements , TokenKind . Unknown , null );
#endregion
var returnPipelineAst = new PipelineAst ( this . Extent , setItemCmdlet );
SetParent ( returnPipelineAst );
return returnPipelineAst ;
}
#endregion
#region static fields / methods
/// <summary>
///
/// </summary>
/// <param name="stmt"></param>
/// <param name="resourceModulePairsToImport">Item1 - ResourceName, Item2 - ModuleName, Item3 - ModuleVersion</param>
/// <returns></returns>
private static bool IsImportCommand ( StatementAst stmt , List < Tuple < string [], ModuleSpecification [], Version >> resourceModulePairsToImport )
{
var dkwsAst = stmt as DynamicKeywordStatementAst ;
if ( dkwsAst == null || ! dkwsAst . Keyword . Keyword . Equals ( "Import-DscResource" , StringComparison . OrdinalIgnoreCase ))
{
return false ;
}
var commandAst = new CommandAst ( dkwsAst . Extent , CopyElements ( dkwsAst . CommandElements ), TokenKind . Unknown , null );
StaticBindingResult bindingResult = StaticParameterBinder . BindCommand ( commandAst , false );
ParameterBindingResult moduleNames = null ;
ParameterBindingResult resourceNames = null ;
ParameterBindingResult moduleVersion = null ;
const string nameParam = "Name" ;
const string moduleNameParam = "ModuleName" ;
const string moduleVersionParam = "ModuleVersion" ;
// we are only interested in Name and ModuleName parameter
foreach ( var entry in bindingResult . BoundParameters )
{
string paramName = entry . Key ;
var paramValue = entry . Value ;
if (( paramName . Length <= nameParam . Length ) && ( paramName . Equals ( nameParam . Substring ( 0 , paramName . Length ), StringComparison . OrdinalIgnoreCase )))
{
resourceNames = paramValue ;
}
// Since both parameters -ModuleName and -ModuleVersion has same start string i.e. Module so we will try to resolve it to -ModuleName
// if user specifies like -Module
if (( paramName . Length <= moduleNameParam . Length ) && ( paramName . Equals ( moduleNameParam . Substring ( 0 , paramName . Length ), StringComparison . OrdinalIgnoreCase )))
{
moduleNames = paramValue ;
}
else if (( paramName . Length <= moduleVersionParam . Length ) && ( paramName . Equals ( moduleVersionParam . Substring ( 0 , paramName . Length ), StringComparison . OrdinalIgnoreCase )))
{
moduleVersion = paramValue ;
}
}
string [] resourceNamesTyped = new [] { "*" };
ModuleSpecification [] moduleNamesTyped = null ;
Version moduleVersionTyped = null ;
if ( resourceNames != null )
{
object resourceNameEvaluated ;
IsConstantValueVisitor . IsConstant ( resourceNames . Value , out resourceNameEvaluated , true , true );
resourceNamesTyped = LanguagePrimitives . ConvertTo < string []>( resourceNameEvaluated );
}
if ( moduleNames != null )
{
object moduleNameEvaluated ;
IsConstantValueVisitor . IsConstant ( moduleNames . Value , out moduleNameEvaluated , true , true );
moduleNamesTyped = LanguagePrimitives . ConvertTo < ModuleSpecification []>( moduleNameEvaluated );
}
if ( moduleVersion != null )
{
object moduleVersionEvaluated ;
IsConstantValueVisitor . IsConstant ( moduleVersion . Value , out moduleVersionEvaluated , true , true );
if ( moduleVersionEvaluated is double )
{
// this happens in case -ModuleVersion 1.0, then use extent text for that.
// The better way to do it would be define static binding API against CommandInfo, that holds information about parameter types.
// This way, we can avoid this ugly special-casing and say that -ModuleVersion has type [System.Version].
moduleVersionEvaluated = moduleVersion . Value . Extent . Text ;
}
moduleVersionTyped = LanguagePrimitives . ConvertTo < Version >( moduleVersionEvaluated );
// Use -ModuleVersion <Version> only in the case, if -ModuleName specified.
// Override ModuleName versions.
if ( moduleNamesTyped != null && moduleNamesTyped . Length == 1 )
{
for ( int i = 0 ; i < moduleNamesTyped . Length ; i ++)
{
moduleNamesTyped [ i ] = new ModuleSpecification ( new Hashtable ()
{
{ "ModuleName" , moduleNamesTyped [ i ]. Name },
{ "ModuleVersion" , moduleVersionTyped }
});
}
}
}
resourceModulePairsToImport . Add ( new Tuple < string [], ModuleSpecification [], Version >( resourceNamesTyped , moduleNamesTyped , moduleVersionTyped ));
return true ;
}
private const string ConfigurationBuildInParametersStr = @"
[cmdletbinding()]
param(
[string]
$InstanceName,
[string[]]
$DependsOn,
[string]
$OutputPath,
[hashtable]
[Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformation()]
$ConfigurationData
)" ;
private static IEnumerable < ParameterAst > ConfigurationBuildInParameters
{
get
{
if ( _configurationBuildInParameters == null )
{
_configurationBuildInParameters = new List < ParameterAst >();
var sb = ScriptBlock . Create ( ConfigurationBuildInParametersStr );
var sba = sb . Ast as ScriptBlockAst ;
if ( sba != null )
{
foreach ( var parameterAst in sba . ParamBlock . Parameters )
{
_configurationBuildInParameters . Add (( ParameterAst ) parameterAst . Copy ());
}
}
}
return _configurationBuildInParameters ;
}
}
private static List < ParameterAst > _configurationBuildInParameters ;
private static IEnumerable < AttributeAst > ConfigurationBuildInParameterAttribAsts
{
get
{
if ( _configurationBuildInParameterAttrAsts == null )
{
_configurationBuildInParameterAttrAsts = new List < AttributeAst >();
var sb = ScriptBlock . Create ( ConfigurationBuildInParametersStr );
var sba = sb . Ast as ScriptBlockAst ;
if ( sba != null )
{
if ( _configurationBuildInParameters == null )
{
_configurationBuildInParameters = new List < ParameterAst >();
foreach ( var parameterAst in sba . ParamBlock . Parameters )
{
_configurationBuildInParameters . Add (( ParameterAst ) parameterAst . Copy ());
}
}
foreach ( var attribAst in sba . ParamBlock . Attributes )
{
_configurationBuildInParameterAttrAsts . Add (( AttributeAst ) attribAst . Copy ());
}
}
}
return _configurationBuildInParameterAttrAsts ;
}
}
private static List < AttributeAst > _configurationBuildInParameterAttrAsts ;
private static IEnumerable < StatementAst > ConfigurationExtraParameterStatements
{
get
{
if ( _configurationExtraParameterStatements == null )
{
_configurationExtraParameterStatements = new List < StatementAst >();
var sb = ScriptBlock . Create ( @"
Import-Module Microsoft.PowerShell.Management -Verbose:$false
Import-Module PSDesiredStateConfiguration -Verbose:$false
$toBody = @{}+$PSBoundParameters
$toBody.Remove(""OutputPath"")
$toBody.Remove(""ConfigurationData"")
$ConfigurationData = $psboundparameters[""ConfigurationData""]
$Outputpath = $psboundparameters[""Outputpath""]" );
var sba = sb . Ast as ScriptBlockAst ;
if ( sba != null )
{
foreach ( var statementAst in sba . EndBlock . Statements )
{
_configurationExtraParameterStatements . Add (( StatementAst ) statementAst . Copy ());
}
}
}
return _configurationExtraParameterStatements ;
}
}
private static List < StatementAst > _configurationExtraParameterStatements ;
#endregion
}
/// <summary>
/// The ast represents the DynamicKeyword statement
/// </summary>
public class DynamicKeywordStatementAst : StatementAst
{
/// <summary>
/// Construct a DynamicKeyword statement
/// </summary>
/// <param name="extent">
/// The extent of the expression, starting with the attribute and ending after the expression being attributed.
/// </param>
/// <param name="commandElements">A collection of <see cref="CommandElementAst"/> used to invoke <see cref="DynamicKeyword"/> specific command</param>
/// <exception cref="PSArgumentException">
/// If <paramref name="commandElements"/> is null or empty.
/// </exception>
public DynamicKeywordStatementAst ( IScriptExtent extent ,
IEnumerable < CommandElementAst > commandElements ) : base ( extent )
{
if ( commandElements == null || commandElements . Count () <= 0 )
{
throw PSTraceSource . NewArgumentException ( "commandElements" );
}
this . CommandElements = new ReadOnlyCollection < CommandElementAst >( commandElements . ToArray ());
SetParents ( CommandElements );
}
/// <summary>
/// A non-empty collection of command elements represent the content of the
/// DynamicKeyword.
/// It may represents a command, such as “Import-DSCResource”,
/// or DSC resources, then CommandElements includes:
/// (1) Keyword Name
/// (2) InstanceName
/// (3) Body, could be ScriptBlockExpressionAst (for Node keyword) or a HashtableAst (remaining)
///
/// This property is never null and never empty
/// </summary>
public ReadOnlyCollection < CommandElementAst > CommandElements { get ; private set ; }
/// <summary>
/// Duplicates the <see cref="DynamicKeywordStatementAst"/>, allowing it to be composed into other ASTs.
/// </summary>
/// <returns>A copy of the <see cref="DynamicKeywordStatementAst"/>, with the link to the previous parent removed.</returns>
public override Ast Copy ()
{
IEnumerable < CommandElementAst > commandElements = CopyElements ( CommandElements );
return new DynamicKeywordStatementAst ( Extent , commandElements )
{
Keyword = this . Keyword ,
LCurly = this . LCurly ,
FunctionName = this . FunctionName ,
InstanceName = CopyElement ( this . InstanceName ),
OriginalInstanceName = CopyElement ( this . OriginalInstanceName ),
BodyExpression = CopyElement ( this . BodyExpression ),
ElementName = this . ElementName ,
};
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
var visitor2 = visitor as ICustomAstVisitor2 ;
return visitor2 != null ? visitor2 . VisitDynamicKeywordStatement ( this ) : null ;
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = AstVisitAction . Continue ;
var visitor2 = visitor as AstVisitor2 ;
if ( visitor2 != null )
{
action = visitor2 . VisitDynamicKeywordStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
}
if ( action == AstVisitAction . Continue )
{
foreach ( CommandElementAst elementAst in CommandElements )
{
action = elementAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue )
break ;
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
/// <summary>
/// Get inferred type of DynamicKeywordStatementAst
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
// TODO: What is the right InferredType for the AST
return CommandElements [ 0 ]. GetInferredType ( context );
}
#region Internal Properties / Methods
internal DynamicKeyword Keyword
{
get
{
return _keyword ;
}
set
{
_keyword = value . Copy ();
}
}
private DynamicKeyword _keyword ;
internal Token LCurly { get ; set ; }
internal Token FunctionName { get ; set ; }
internal ExpressionAst InstanceName { get ; set ; }
internal ExpressionAst OriginalInstanceName { get ; set ; }
internal ExpressionAst BodyExpression { get ; set ; }
internal string ElementName { get ; set ; }
private PipelineAst _commandCallPipelineAst ;
internal PipelineAst GenerateCommandCallPipelineAst ()
{
if ( _commandCallPipelineAst != null )
return _commandCallPipelineAst ;
/////////////////////////////////////////////////////////////////////////
//
// Now construct the AST to call the function that defines implements the keywords logic. There are
// two different types of ASTs that may be generated, depending on the settings in the DynamicKeyword object.
// The first type uses a fixed set of 4 arguments and has the command with the signature
// function moduleName\keyword
// {
// param (
// $KeywordData, # the DynamicKeyword object processed by this rule.
// $Name, # the value of the name expression syntax element. This may be null for keywords that don't take a name
// $Value, # the body of the keyword - either a hashtable or a scriptblock
// $SourceMetadata # a string containing the original source line information so that errors
// )
// # function logic...
// }
//
// The second type, where the DirectCall flag is set to true, simply calls the command directly.
// In the original source, the keyword body will be a property collection where the allowed properties
// in the collection corresond to the parameters on the actual called function.
//
var cea = new Collection < CommandElementAst >();
//
// First add the name of the command to call. If a module name has been provided
// then use the module-quilified form of the command name..
//
if ( string . IsNullOrEmpty ( Keyword . ImplementingModule ))
{
cea . Add (
new StringConstantExpressionAst (
FunctionName . Extent ,
FunctionName . Text ,
StringConstantType . BareWord ));
}
else
{
cea . Add (
new StringConstantExpressionAst (
FunctionName . Extent ,
Keyword . ImplementingModule + '\\' + FunctionName . Text ,
StringConstantType . BareWord ));
}
ExpressionAst expr = BodyExpression ;
HashtableAst hashtable = expr as HashtableAst ;
if ( Keyword . DirectCall )
{
// If this keyword takes a name, then add it as the parameter -InstanceName
if ( Keyword . NameMode != DynamicKeywordNameMode . NoName )
{
cea . Add (
new CommandParameterAst (
FunctionName . Extent ,
"InstanceName" ,
InstanceName ,
FunctionName . Extent ));
}
//
// If it's a direct call keyword, then we just unravel the properties
// in the hash literal expression and map them to parameters.
// We've already checked to make sure that they're all valid names.
//
if ( hashtable != null )
{
bool isHashtableValid = true ;
//
// If it's a hash table, validate that only valid members have been specified.
//
foreach ( var keyValueTuple in hashtable . KeyValuePairs )
{
var propName = keyValueTuple . Item1 as StringConstantExpressionAst ;
if ( propName == null )
{
isHashtableValid = false ;
break ;
}
else
{
if (! Keyword . Properties . ContainsKey ( propName . Value ))
{
isHashtableValid = false ;
break ;
}
}
if ( keyValueTuple . Item2 is ErrorStatementAst )
{
isHashtableValid = false ;
break ;
}
}
if ( isHashtableValid )
{
// Construct the real parameters if Hashtable is valid
foreach ( var keyValueTuple in hashtable . KeyValuePairs )
{
var propName = ( StringConstantExpressionAst ) keyValueTuple . Item1 ;
ExpressionAst propValue = new SubExpressionAst (
FunctionName . Extent ,
new StatementBlockAst (
FunctionName . Extent ,
new StatementAst [] { ( StatementAst ) keyValueTuple . Item2 . Copy () }, null ));
cea . Add (
new CommandParameterAst (
FunctionName . Extent ,
propName . Value ,
propValue ,
LCurly . Extent ));
}
}
else
{
// Construct a fake parameter with the HashtableAst to be its value, so that
// tab completion on the property names would work.
cea . Add (
new CommandParameterAst (
FunctionName . Extent ,
"InvalidPropertyHashtable" ,
hashtable ,
LCurly . Extent ));
}
}
}
else
{
//
// Add the -KeywordData parameter using the expression
// ([type]("System.Management.Automation.Language.DynamicKeyword"))::GetKeyword(name)
// To invoke the method to get the keyword data object for that keyword.
//
var indexExpr = new InvokeMemberExpressionAst (
FunctionName . Extent ,
new TypeExpressionAst (
FunctionName . Extent ,
new TypeName (
FunctionName . Extent ,
typeof ( System . Management . Automation . Language . DynamicKeyword ). FullName )),
new StringConstantExpressionAst (
FunctionName . Extent ,
"GetKeyword" ,
StringConstantType . BareWord ),
new List < ExpressionAst >
{
new StringConstantExpressionAst (
FunctionName . Extent ,
FunctionName . Text ,
StringConstantType . BareWord )
},
true );
cea . Add (
new CommandParameterAst (
FunctionName . Extent ,
"KeywordData" ,
indexExpr ,
LCurly . Extent ));
//
// Add the -Name parameter
//
cea . Add (
new CommandParameterAst (
FunctionName . Extent ,
"Name" ,
InstanceName ,
LCurly . Extent ));
//
// Add the -Value parameter
//
cea . Add (
new CommandParameterAst (
LCurly . Extent ,
"Value" ,
expr ,
LCurly . Extent ));
//
// Add the -SourceMetadata parametere
//
string sourceMetadata = FunctionName . Extent . File
+ "::" + FunctionName . Extent . StartLineNumber
+ "::" + FunctionName . Extent . StartColumnNumber
+ "::" + FunctionName . Extent . Text ;
cea . Add (
new CommandParameterAst (
LCurly . Extent , "SourceMetadata" ,
new StringConstantExpressionAst (
FunctionName . Extent ,
sourceMetadata ,
StringConstantType . BareWord ),
LCurly . Extent ));
}
//
// Build the final statement - a pipeline containing a single element with is a CommandAst
// containing the command we've built up.
//
var cmdAst = new CommandAst ( FunctionName . Extent , cea , TokenKind . Unknown , null );
cmdAst . DefiningKeyword = Keyword ;
_commandCallPipelineAst = new PipelineAst ( FunctionName . Extent , cmdAst );
return _commandCallPipelineAst ;
}
#endregion
}
#endregion Statements
#region Expressions
/// <summary>
/// An abstract base class that represents all PowerShell expressions.
/// </summary>
public abstract class ExpressionAst : CommandElementAst
{
/// <summary>
/// Initialize the fields common to all expressions.
/// </summary>
/// <param name="extent">The extent of the expression.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
protected ExpressionAst ( IScriptExtent extent )
: base ( extent )
{
}
/// <summary>
/// By default, the static type of an expression is unknown and hence we assume <c>typeof(object)</c>.
/// </summary>
public virtual Type StaticType { get { return typeof ( object ); } }
/// <summary>
/// Determine if the results of ParenExpression/SubExpression should be preserved in case of exception
/// </summary>
/// <remarks>
/// We should preserve the partial output in case of exception only if the SubExpression/ParenExpression meets following conditions:
/// 1. the SubExpr/ParenExpr is the first expression, and the only element in a pipeline
/// 2. the pipeline's parent is a StatementBlockAst or NamedBlockAst. e.g. $(1; throw 2) OR if (true) { $(1; throw 2) }
/// </remarks>
/// <returns></returns>
internal virtual bool ShouldPreserveOutputInCaseOfException ()
{
var parenExpr = this as ParenExpressionAst ;
var subExpr = this as SubExpressionAst ;
if ( parenExpr == null && subExpr == null )
{
PSTraceSource . NewInvalidOperationException ();
}
var commandExpr = this . Parent as CommandExpressionAst ;
if ( commandExpr == null )
{
return false ;
}
var pipelineAst = commandExpr . Parent as PipelineAst ;
if ( pipelineAst == null || pipelineAst . PipelineElements . Count > 1 )
{
return false ;
}
var parenExpressionAst = pipelineAst . Parent as ParenExpressionAst ;
if ( parenExpressionAst != null )
{
return parenExpressionAst . ShouldPreserveOutputInCaseOfException ();
}
else
{
return ( pipelineAst . Parent is StatementBlockAst || pipelineAst . Parent is NamedBlockAst );
}
}
}
/// <summary>
/// The ast representing a binary expression, e.g. <c>$a + $b</c>.
/// </summary>
public class BinaryExpressionAst : ExpressionAst
{
/// <summary>
/// Construct a binary expression.
/// </summary>
/// <param name="extent">The extent of the expression.</param>
/// <param name="left">The left hand operand.</param>
/// <param name="operator">The binary operator.</param>
/// <param name="right">The right hand operand.</param>
/// <param name="errorPosition">
/// The position to report if an error occurs at runtime while evaluating the binary operation.
/// </param>
/// <exception cref="PSArgumentException">
/// If <paramref name="operator"/> is not a valid binary operator.
/// </exception>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="left"/>, <paramref name="right"/>,
/// or <paramref name="errorPosition"/> is null.
/// </exception>
public BinaryExpressionAst ( IScriptExtent extent , ExpressionAst left , TokenKind @operator , ExpressionAst right , IScriptExtent errorPosition )
: base ( extent )
{
if (( @operator . GetTraits () & TokenFlags . BinaryOperator ) == 0 )
{
throw PSTraceSource . NewArgumentException ( "operator" );
}
if ( left == null || right == null || errorPosition == null )
{
throw PSTraceSource . NewArgumentNullException ( left == null ? "left" : right == null ? "right" : "errorPosition" );
}
this . Left = left ;
SetParent ( left );
this . Operator = @operator ;
this . Right = right ;
SetParent ( right );
this . ErrorPosition = errorPosition ;
}
/// <summary>
/// The operator token kind. The value returned is always a binary operator.
/// </summary>
public TokenKind Operator { get ; private set ; }
/// <summary>
/// The ast for the left hand side of the binary expression. The property is never null.
/// </summary>
public ExpressionAst Left { get ; private set ; }
/// <summary>
/// The ast for the right hand side of the binary expression. The property is never null.
/// </summary>
public ExpressionAst Right { get ; private set ; }
/// <summary>
/// The position to report an error if an error occurs at runtime. The property is never null.
/// </summary>
public IScriptExtent ErrorPosition { get ; private set ; }
/// <summary>
/// Copy the BinaryExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newLeft = CopyElement ( this . Left );
var newRight = CopyElement ( this . Right );
return new BinaryExpressionAst ( this . Extent , newLeft , this . Operator , newRight , this . ErrorPosition );
}
/// <summary>
/// The result type of the operation. For most binary operators, the type is unknown until runtime, but
/// xor always results in <c>typeof(bool)</c>.
/// </summary>
public override Type StaticType
{
get
{
switch ( Operator )
{
case TokenKind . Xor :
case TokenKind . And :
case TokenKind . Or :
case TokenKind . Is :
return typeof ( bool );
}
return typeof ( object );
}
}
internal static readonly PSTypeName [] BoolTypeNameArray = new PSTypeName [] { new PSTypeName ( typeof ( bool )) };
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
switch ( Operator )
{
case TokenKind . Xor :
case TokenKind . And :
case TokenKind . Or :
case TokenKind . Is :
return BoolTypeNameArray ;
}
// This may not be right, but we're guessing anyway, and it's not a bad guess.
return Left . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitBinaryExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitBinaryExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Left . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Right . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast representing an expression with a unary operator.
/// </summary>
public class UnaryExpressionAst : ExpressionAst
{
/// <summary>
/// Construct a unary expression.
/// </summary>
/// <param name="extent">The extent of the expression, including the operator (which may be prefix or postfix.)</param>
/// <param name="tokenKind">The unary operator token kind for the operation.</param>
/// <param name="child">The expression that the unary operator is applied to.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="child"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="tokenKind"/> is not a valid unary operator.
/// </exception>
public UnaryExpressionAst ( IScriptExtent extent , TokenKind tokenKind , ExpressionAst child )
: base ( extent )
{
if (( tokenKind . GetTraits () & TokenFlags . UnaryOperator ) == 0 )
{
throw PSTraceSource . NewArgumentException ( "tokenKind" );
}
if ( child == null )
{
throw PSTraceSource . NewArgumentNullException ( "child" );
}
this . TokenKind = tokenKind ;
this . Child = child ;
SetParent ( child );
}
/// <summary>
/// The operator token for the unary expression. The value returned is always a unary operator.
/// </summary>
public TokenKind TokenKind { get ; private set ; }
/// <summary>
/// The child expression the unary operator is applied to. The property is never null.
/// </summary>
public ExpressionAst Child { get ; private set ; }
/// <summary>
/// Copy the UnaryExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newChild = CopyElement ( this . Child );
return new UnaryExpressionAst ( this . Extent , this . TokenKind , newChild );
}
/// <summary>
/// Returns <c>typeof(bool)</c> if the unary operator is a logical negation, otherwise returns <c>typeof(object)</c>.
/// </summary>
public override Type StaticType
{
get
{
return ( TokenKind == TokenKind . Not || TokenKind == TokenKind . Exclaim )
? typeof ( bool )
: typeof ( object );
}
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return ( TokenKind == TokenKind . Not || TokenKind == TokenKind . Exclaim )
? BinaryExpressionAst . BoolTypeNameArray
: Child . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitUnaryExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitUnaryExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Child . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents a scriptblock with a keyword name. This is normally allowed only for script workflow.
/// e.g. <c>parallel { ... }</c> or <c>sequence { ... }</c>.
/// </summary>
public class BlockStatementAst : StatementAst
{
/// <summary>
/// Construct a keyword block expression
/// </summary>
/// <param name="extent"></param>
/// <param name="kind"></param>
/// <param name="body"></param>
public BlockStatementAst ( IScriptExtent extent , Token kind , StatementBlockAst body )
: base ( extent )
{
if ( kind == null || body == null )
{
throw PSTraceSource . NewArgumentNullException ( kind == null ? "kind" : "body" );
}
if ( kind . Kind != TokenKind . Sequence && kind . Kind != TokenKind . Parallel )
{
throw PSTraceSource . NewArgumentException ( "kind" );
}
this . Kind = kind ;
this . Body = body ;
SetParent ( body );
}
/// <summary>
/// The scriptblockexpression that has a keyword applied to it. This property is nerver null.
/// </summary>
public StatementBlockAst Body { get ; private set ; }
/// <summary>
/// The keyword name
/// </summary>
public Token Kind { get ; private set ; }
/// <summary>
/// Copy the BlockStatementAst instance
/// </summary>
public override Ast Copy ()
{
var newBody = CopyElement ( this . Body );
return new BlockStatementAst ( this . Extent , this . Kind , newBody );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Body . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitBlockStatement ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitBlockStatement ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Body . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents an expression with an attribute. This is normally allowed only on parameters or variables
/// being assigned, e.g. <c>[Parameter()]$PassThru</c> or <c>[ValidateScript({$true})$abc = 42</c>.
/// </summary>
public class AttributedExpressionAst : ExpressionAst , ISupportsAssignment , IAssignableValue
{
/// <summary>
/// Construct an attributed expression.
/// </summary>
/// <param name="extent">
/// The extent of the expression, starting with the attribute and ending after the expression being attributed.
/// </param>
/// <param name="attribute">The attribute being applied to <paramref name="child"/>.</param>
/// <param name="child">The expression being attributed by <paramref name="attribute"/>.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="attribute"/>, or <paramref name="child"/> is null.
/// </exception>
public AttributedExpressionAst ( IScriptExtent extent , AttributeBaseAst attribute , ExpressionAst child )
: base ( extent )
{
if ( attribute == null || child == null )
{
throw PSTraceSource . NewArgumentNullException ( attribute == null ? "attribute" : "child" );
}
this . Attribute = attribute ;
SetParent ( attribute );
this . Child = child ;
SetParent ( child );
}
/// <summary>
/// The expression that has an attribute or type constraint applied to it. This property is never null.
/// </summary>
public ExpressionAst Child { get ; private set ; }
/// <summary>
/// The attribute or type constraint for this expression. This property is never null.
/// </summary>
public AttributeBaseAst Attribute { get ; private set ; }
/// <summary>
/// Copy the AttributedExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newAttribute = CopyElement ( this . Attribute );
var newChild = CopyElement ( this . Child );
return new AttributedExpressionAst ( this . Extent , newAttribute , newChild );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Child . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitAttributedExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitAttributedExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Attribute . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Child . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
#region Code Generation Details
ISupportsAssignment GetActualAssignableAst ()
{
ExpressionAst child = this ;
var childAttributeAst = child as AttributedExpressionAst ;
while ( childAttributeAst != null )
{
child = childAttributeAst . Child ;
childAttributeAst = child as AttributedExpressionAst ;
}
// Semantic checks ensure this cast succeeds
return ( ISupportsAssignment ) child ;
}
List < AttributeBaseAst > GetAttributes ()
{
var attributes = new List < AttributeBaseAst >();
var childAttributeAst = this ;
while ( childAttributeAst != null )
{
attributes . Add ( childAttributeAst . Attribute );
childAttributeAst = childAttributeAst . Child as AttributedExpressionAst ;
}
attributes . Reverse ();
return attributes ;
}
IAssignableValue ISupportsAssignment . GetAssignableValue ()
{
return this ;
}
Expression IAssignableValue . GetValue ( Compiler compiler , List < Expression > exprs , List < ParameterExpression > temps )
{
return ( Expression ) this . Accept ( compiler );
}
Expression IAssignableValue . SetValue ( Compiler compiler , Expression rhs )
{
var attributes = GetAttributes ();
var assignableValue = GetActualAssignableAst (). GetAssignableValue ();
var variableExpr = assignableValue as VariableExpressionAst ;
if ( variableExpr == null )
{
return assignableValue . SetValue ( compiler , Compiler . ConvertValue ( rhs , attributes ));
}
return Compiler . CallSetVariable ( Expression . Constant ( variableExpr . VariablePath ), rhs , Expression . Constant ( attributes . ToArray ()));
}
#endregion Code Generation Details
}
/// <summary>
/// The ast that represents a cast expression, e.g. <c>[wmiclass]"Win32_Process"</c>.
/// </summary>
public class ConvertExpressionAst : AttributedExpressionAst , ISupportsAssignment
{
/// <summary>
/// Construct a cast expression.
/// </summary>
/// <param name="extent">
/// The extent of the expression, starting with the type literal and ending after the expression being converted.
/// </param>
/// <param name="typeConstraint">The type to convert to.</param>
/// <param name="child">The expression being converted.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="typeConstraint"/>, or <paramref name="child"/> is null.
/// </exception>
public ConvertExpressionAst ( IScriptExtent extent , TypeConstraintAst typeConstraint , ExpressionAst child )
: base ( extent , typeConstraint , child )
{
}
/// <summary>
/// The type to convert to.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
public TypeConstraintAst Type { get { return ( TypeConstraintAst ) Attribute ; } }
/// <summary>
/// Copy the ConvertExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newTypeConstraint = CopyElement ( this . Type );
var newChild = CopyElement ( this . Child );
return new ConvertExpressionAst ( this . Extent , newTypeConstraint , newChild );
}
/// <summary>
/// The static type produced after the cast is normally the type named by <see cref="Type"/>, but in some cases
/// it may not be, in which, <see cref="Object"/> is assumed.
/// </summary>
public override Type StaticType
{
get { return this . Type . TypeName . GetReflectionType () ?? typeof ( object ); }
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
var type = this . Type . TypeName . GetReflectionType ();
if ( type != null )
{
yield return new PSTypeName ( type );
}
else
{
yield return new PSTypeName ( this . Type . TypeName . FullName );
}
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitConvertExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitConvertExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Type . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Child . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
#region Code Generation Details
IAssignableValue ISupportsAssignment . GetAssignableValue ()
{
var varExpr = Child as VariableExpressionAst ;
if ( varExpr != null && varExpr . TupleIndex >= 0 )
{
// In the common case of a single cast on the lhs of an assignment, we may have saved the type of the
// variable in the mutable tuple, so conversions will get generated elsewhere, and we can just use
// the child as the assignable value.
return varExpr ;
}
return this ;
}
internal bool IsRef ()
{
return Type . TypeName . Name . Equals ( "ref" , StringComparison . OrdinalIgnoreCase );
}
#endregion Code Generation Details
}
/// <summary>
/// The ast that represents accessing a member as a property, e.g. <c>$x.Length</c> or <c>[int]::MaxValue</c>.
/// Most often this is a simple property access, but methods can also be access in this manner, returning an object
/// that supports invoking that member.
/// </summary>
public class MemberExpressionAst : ExpressionAst , ISupportsAssignment
{
/// <summary>
/// Construct an ast to reference a property.
/// </summary>
/// <param name="extent">
/// The extent of the expression, starting with the expression before the operator '.' or '::' and ending after
/// membername or expression naming the member.
/// </param>
/// <param name="expression">The expression before the member access operator '.' or '::'.</param>
/// <param name="member">The name or expression naming the member to access.</param>
/// <param name="static">True if the '::' operator was used, false if '.' is used.
/// True if the member access is for a static member, using '::', false if accessing a member on an instace using '.'.
/// </param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="expression"/>, or <paramref name="member"/> is null.
/// </exception>
public MemberExpressionAst ( IScriptExtent extent , ExpressionAst expression , CommandElementAst member , bool @static )
: base ( extent )
{
if ( expression == null || member == null )
{
throw PSTraceSource . NewArgumentNullException ( expression == null ? "expression" : "member" );
}
this . Expression = expression ;
SetParent ( expression );
this . Member = member ;
SetParent ( member );
this . Static = @static ;
}
/// <summary>
/// The expression that produces the value to retrieve the member from. This property is never null.
/// </summary>
public ExpressionAst Expression { get ; private set ; }
/// <summary>
/// The name of the member to retrieve. This property is never null.
/// </summary>
public CommandElementAst Member { get ; private set ; }
/// <summary>
/// True if the member to return is static, false if the member is an instance member.
/// </summary>
public bool Static { get ; private set ; }
/// <summary>
/// Copy the MemberExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newExpression = CopyElement ( this . Expression );
var newMember = CopyElement ( this . Member );
return new MemberExpressionAst ( this . Extent , newExpression , newMember , this . Static );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
// If the member name isn't simple, don't even try.
var memberAsStringConst = Member as StringConstantExpressionAst ;
if ( memberAsStringConst == null )
yield break ;
PSTypeName [] exprType ;
if ( this . Static )
{
var exprAsType = Expression as TypeExpressionAst ;
if ( exprAsType == null )
yield break ;
var type = exprAsType . TypeName . GetReflectionType ();
if ( type == null )
{
var typeName = exprAsType . TypeName as TypeName ;
if ( typeName == null || typeName . _typeDefinitionAst == null )
yield break ;
exprType = new [] { new PSTypeName ( typeName . _typeDefinitionAst ) };
}
else
{
exprType = new [] { new PSTypeName ( type ) };
}
}
else
{
exprType = Expression . GetInferredType ( context ). ToArray ();
if ( exprType . Length == 0 )
yield break ;
}
var maybeWantDefaultCtor = this . Static
&& this is InvokeMemberExpressionAst
&& memberAsStringConst . Value . Equals ( "new" , StringComparison . OrdinalIgnoreCase );
// We use a list of member names because we might discover aliases properties
// and if we do, we'll add to the list.
var memberNameList = new List < string > { memberAsStringConst . Value };
foreach ( var type in exprType )
{
var members = CompletionCompleters . GetMembersByInferredType ( type , context , this . Static , filter : null );
for ( int i = 0 ; i < memberNameList . Count ; i ++)
{
string memberName = memberNameList [ i ];
foreach ( var member in members )
{
var propertyInfo = member as PropertyInfo ;
if ( propertyInfo != null )
{
if ( propertyInfo . Name . Equals ( memberName , StringComparison . OrdinalIgnoreCase ) &&
!( this is InvokeMemberExpressionAst ))
{
yield return new PSTypeName ( propertyInfo . PropertyType );
break ;
}
continue ;
}
var fieldInfo = member as FieldInfo ;
if ( fieldInfo != null )
{
if ( fieldInfo . Name . Equals ( memberName , StringComparison . OrdinalIgnoreCase ) &&
!( this is InvokeMemberExpressionAst ))
{
yield return new PSTypeName ( fieldInfo . FieldType );
break ;
}
continue ;
}
var methodCacheEntry = member as DotNetAdapter . MethodCacheEntry ;
if ( methodCacheEntry != null )
{
if ( methodCacheEntry [ 0 ]. method . Name . Equals ( memberName , StringComparison . OrdinalIgnoreCase ))
{
maybeWantDefaultCtor = false ;
if ( this is InvokeMemberExpressionAst )
{
foreach ( var method in methodCacheEntry . methodInformationStructures )
{
var methodInfo = method . method as MethodInfo ;
if ( methodInfo != null && ! methodInfo . ReturnType . GetTypeInfo (). ContainsGenericParameters )
{
yield return new PSTypeName ( methodInfo . ReturnType );
}
}
}
else
{
// Accessing a method as a property, we'd return a wrapper over the method.
yield return new PSTypeName ( typeof ( PSMethod ));
}
break ;
}
continue ;
}
var memberAst = member as MemberAst ;
if ( memberAst != null )
{
if ( memberAst . Name . Equals ( memberName , StringComparison . OrdinalIgnoreCase ))
{
if ( this is InvokeMemberExpressionAst )
{
var functionMemberAst = memberAst as FunctionMemberAst ;
if ( functionMemberAst != null && ! functionMemberAst . IsReturnTypeVoid ())
{
yield return new PSTypeName ( functionMemberAst . ReturnType . TypeName );
}
}
else
{
var propertyMemberAst = memberAst as PropertyMemberAst ;
if ( propertyMemberAst != null )
{
if ( propertyMemberAst . PropertyType != null )
{
yield return new PSTypeName ( propertyMemberAst . PropertyType . TypeName );
}
else
{
yield return new PSTypeName ( typeof ( object ));
}
}
else
{
// Accessing a method as a property, we'd return a wrapper over the method.
yield return new PSTypeName ( typeof ( PSMethod ));
}
}
}
continue ;
}
var memberInfo = member as PSMemberInfo ;
if ( memberInfo == null ||
! memberInfo . Name . Equals ( memberName , StringComparison . OrdinalIgnoreCase ))
{
continue ;
}
var noteProperty = member as PSNoteProperty ;
if ( noteProperty != null )
{
yield return new PSTypeName ( noteProperty . Value . GetType ());
break ;
}
var aliasProperty = member as PSAliasProperty ;
if ( aliasProperty != null )
{
memberNameList . Add ( aliasProperty . ReferencedMemberName );
break ;
}
var codeProperty = member as PSCodeProperty ;
if ( codeProperty != null )
{
if ( codeProperty . GetterCodeReference != null )
{
yield return new PSTypeName ( codeProperty . GetterCodeReference . ReturnType );
}
break ;
}
ScriptBlock scriptBlock = null ;
var scriptProperty = member as PSScriptProperty ;
if ( scriptProperty != null )
{
scriptBlock = scriptProperty . GetterScript ;
}
var scriptMethod = member as PSScriptMethod ;
if ( scriptMethod != null )
{
scriptBlock = scriptMethod . Script ;
}
if ( scriptBlock != null )
{
foreach ( var t in scriptBlock . OutputType )
{
yield return t ;
}
}
break ;
}
}
// We didn't find any constructors but they used [T]::new() syntax
if ( maybeWantDefaultCtor )
{
yield return type ;
}
}
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitMemberExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitMemberExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Expression . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Member . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
IAssignableValue ISupportsAssignment . GetAssignableValue ()
{
return new MemberAssignableValue { MemberExpression = this };
}
}
/// <summary>
/// The ast that represents the invocation of a method, e.g. <c>$sb.Append('abc')</c> or <c>[math]::Sign($i)</c>.
/// </summary>
public class InvokeMemberExpressionAst : MemberExpressionAst , ISupportsAssignment
{
/// <summary>
/// Construct an instance of a method invocation expression.
/// </summary>
/// <param name="extent">
/// The extent of the expression, starting with the expression before the invocation operator and ending with the
/// closing paren after the arguments.
/// </param>
/// <param name="expression">The expression before the invocation operator ('.' or '::').</param>
/// <param name="method">The method to invoke.</param>
/// <param name="arguments">The arguments to pass to the method.</param>
/// <param name="static">
/// True if the invocation is for a static method, using '::', false if invoking a method on an instace using '.'.
/// </param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public InvokeMemberExpressionAst ( IScriptExtent extent , ExpressionAst expression , CommandElementAst method , IEnumerable < ExpressionAst > arguments , bool @static )
: base ( extent , expression , method , @static )
{
if ( arguments != null && arguments . Any ())
{
this . Arguments = new ReadOnlyCollection < ExpressionAst >( arguments . ToArray ());
SetParents ( Arguments );
}
}
/// <summary>
/// The non-empty collection of arguments to pass when invoking the method, or null if no arguments were specified.
/// </summary>
public ReadOnlyCollection < ExpressionAst > Arguments { get ; private set ; }
/// <summary>
/// Copy the InvokeMemberExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newExpression = CopyElement ( this . Expression );
var newMethod = CopyElement ( this . Member );
var newArguments = CopyElements ( this . Arguments );
return new InvokeMemberExpressionAst ( this . Extent , newExpression , newMethod , newArguments , this . Static );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitInvokeMemberExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitInvokeMemberExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = this . InternalVisitChildren ( visitor );
return visitor . CheckForPostAction ( this , action );
}
internal AstVisitAction InternalVisitChildren ( AstVisitor visitor )
{
var action = Expression . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Member . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue && Arguments != null )
{
for ( int index = 0 ; index < Arguments . Count ; index ++)
{
var arg = Arguments [ index ];
action = arg . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return action ;
}
#endregion Visitors
IAssignableValue ISupportsAssignment . GetAssignableValue ()
{
return new InvokeMemberAssignableValue { InvokeMemberExpressionAst = this };
}
}
/// <summary>
/// The ast that represents the invocation of a base ctor method from PS class instance ctor, e.g. <c>class B : A{ B() : base() {} }</c>.
/// </summary>
public class BaseCtorInvokeMemberExpressionAst : InvokeMemberExpressionAst
{
/// <summary>
/// Construct an instance of a base ctor invocation expression.
/// </summary>
/// <param name="baseKeywordExtent">
/// The extent of the base keyword, i.e. for
/// <c>class B : A { B() : base(100) {} }</c>
/// it will be "base".
/// Can be empty extent (i.e. for implicit base ctor call).
/// </param>
/// <param name="baseCallExtent">
/// The extent of the base ctor call expression, i.e. for
/// <c>class B : A { B() : base(100) {} }</c>
/// it will be "base(100)"
/// Can be empty extent (i.e. for implicit base ctor call).
/// </param>
/// <param name="arguments">The arguments to pass to the ctor.</param>
public BaseCtorInvokeMemberExpressionAst ( IScriptExtent baseKeywordExtent , IScriptExtent baseCallExtent , IEnumerable < ExpressionAst > arguments )
: base (
baseCallExtent ,
new VariableExpressionAst ( baseKeywordExtent , "this" , false ),
new StringConstantExpressionAst ( baseKeywordExtent , ".ctor" , StringConstantType . BareWord ),
arguments ,
@static : false )
{
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
AstVisitAction action = AstVisitAction . Continue ;
var visitor2 = visitor as AstVisitor2 ;
if ( visitor2 != null )
{
action = visitor2 . VisitBaseCtorInvokeMemberExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
}
if ( action == AstVisitAction . Continue )
action = this . InternalVisitChildren ( visitor );
return visitor . CheckForPostAction ( this , action );
}
internal override object Accept ( ICustomAstVisitor visitor )
{
var visitor2 = visitor as ICustomAstVisitor2 ;
return visitor2 != null ? visitor2 . VisitBaseCtorInvokeMemberExpression ( this ) : null ;
}
}
/// <summary>
/// The name and attributes of a type.
/// </summary>
public interface ITypeName
{
/// <summary>
/// The full name of the type, including any namespace and assembly name.
/// </summary>
string FullName { get ; }
/// <summary>
/// The name of the type, including any namespace, but not including the assembly name.
/// </summary>
string Name { get ; }
/// <summary>
/// The name of the assembly, if specified, otherwise null.
/// </summary>
string AssemblyName { get ; }
/// <summary>
/// Returns true if the type names an array, false otherwise.
/// </summary>
bool IsArray { get ; }
/// <summary>
/// Returns true if the type names a closed generic type (has generic type arguments), false otherwise.
/// </summary>
bool IsGeneric { get ; }
/// <summary>
/// Returns the <see cref="System.Type"/> that this typename represents, if such a type exists, null otherwise.
/// </summary>
Type GetReflectionType ();
/// <summary>
/// Assuming the typename is an attribute, returns the <see cref="System.Type"/> that this typename represents.
/// By convention, the typename may omit the suffix "Attribute". Lookup will attempt to resolve the type as is,
/// and if that fails, the suffix "Attribute" will be appended.
/// </summary>
Type GetReflectionAttributeType ();
/// <summary>
/// The extent of the typename.
/// </summary>
IScriptExtent Extent { get ; }
}
internal interface ISupportsTypeCaching
{
Type CachedType { get ; set ; }
}
/// <summary>
/// A simple type that is not an array or does not have generic arguments.
/// </summary>
public sealed class TypeName : ITypeName , ISupportsTypeCaching
{
internal readonly string _name ;
internal Type _type ;
internal readonly IScriptExtent _extent ;
internal TypeDefinitionAst _typeDefinitionAst ;
/// <summary>
/// Construct a simple typename.
/// </summary>
/// <param name="extent">The extent of the typename.</param>
/// <param name="name">The name of the type.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="name"/> is null or the empty string.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="name"/> contains characters that are only allowed in a generic or array typename.
/// </exception>
public TypeName ( IScriptExtent extent , string name )
{
if ( extent == null || string . IsNullOrEmpty ( name ))
{
throw PSTraceSource . NewArgumentNullException ( extent == null ? "extent" : "name" );
}
var c = name [ 0 ];
if ( c == '[' || c == ']' || c == ',' )
{
throw PSTraceSource . NewArgumentException ( "name" );
}
int backtick = name . IndexOf ( '`' );
if ( backtick != - 1 )
{
name = name . Replace ( "``" , "`" );
}
this . _extent = extent ;
this . _name = name ;
}
/// <summary>
/// Construct a typename with an assembly specification.
/// </summary>
/// <param name="extent">The extent of the typename.</param>
/// <param name="name">The name of the type.</param>
/// <param name="assembly">The assembly the type belongs to.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null or if <paramref name="name"/> or <paramref name="assembly"/> is null or the empty string.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="name"/> contains characters that are only allowed in a generic or array typename.
/// </exception>
public TypeName ( IScriptExtent extent , string name , string assembly )
: this ( extent , name )
{
if ( string . IsNullOrEmpty ( assembly ))
{
throw PSTraceSource . NewArgumentNullException ( "assembly" );
}
AssemblyName = assembly ;
}
/// <summary>
/// Returns the full name of the type.
/// </summary>
public string FullName { get { return AssemblyName != null ? _name + "," + AssemblyName : _name ; } }
/// <summary>
/// Returns the name of the type, w/o any assembly name if one was specified.
/// </summary>
public string Name { get { return _name ; } }
/// <summary>
/// The name of the assembly, if specified, otherwise null.
/// </summary>
public string AssemblyName { get ; internal set ; }
/// <summary>
/// Always returns false, array typenames are instances of <see cref="ArrayTypeName"/>.
/// </summary>
public bool IsArray { get { return false ; } }
/// <summary>
/// Always returns false, generic typenames are instances of <see cref="GenericTypeName"/>.
/// </summary>
public bool IsGeneric { get { return false ; } }
/// <summary>
/// The extent of the typename.
/// </summary>
public IScriptExtent Extent { get { return _extent ; } }
internal bool HasDefaultCtor ()
{
if ( _typeDefinitionAst == null )
{
Type reflectionType = GetReflectionType ();
if ( reflectionType == null )
{
// we are pessimistic about default ctor presence.
return false ;
}
return reflectionType . HasDefaultCtor ();
}
bool hasExplicitCtor = false ;
foreach ( var member in _typeDefinitionAst . Members )
{
var function = member as FunctionMemberAst ;
if ( function != null )
{
if ( function . IsConstructor )
{
// TODO: add check for default values, once default values for parameters supported
if (! function . Parameters . Any ())
{
return true ;
}
hasExplicitCtor = true ;
}
}
}
// implicit ctor is default as well
return ! hasExplicitCtor ;
}
/// <summary>
/// Get the <see cref="Type"/> from a typename.
/// </summary>
/// <returns>
/// The <see cref="Type"/> if possible, null otherwise. Null may be returned for valid typenames if the assembly
/// containing the type has not been loaded.
/// </returns>
public Type GetReflectionType ()
{
if ( _type == null )
{
Exception e ;
Type type = _typeDefinitionAst != null ? _typeDefinitionAst . Type : TypeResolver . ResolveTypeName ( this , out e );
if ( type != null )
{
try
{
var unused = type . TypeHandle ;
}
catch ( NotSupportedException )
{
// If the value of 'type' comes from typeBuilder.AsType(), then the value of 'type.GetTypeInfo()'
// is actually the TypeBuilder instance itself. This is the same on both FullCLR and CoreCLR.
Diagnostics . Assert ( _typeDefinitionAst != null , "_typeDefinitionAst can never be null" );
return type ;
}
Interlocked . CompareExchange ( ref _type , type , null );
}
}
return _type ;
}
/// <summary>
/// Returns the <see cref="Type"/> this type represents, assuming the type is an attribute. The suffix
/// "Attribute" may be appended, if necessary, to resolve the type.
/// </summary>
/// <returns>
/// The <see cref="Type"/> if possible, null otherwise. Null may be returned for valid typenames if the assembly
/// containing the type has not been loaded.
/// </returns>
public Type GetReflectionAttributeType ()
{
var result = GetReflectionType ();
if ( result == null || ! typeof ( Attribute ). IsAssignableFrom ( result ))
{
var attrTypeName = new TypeName ( _extent , FullName + "Attribute" );
result = attrTypeName . GetReflectionType ();
if ( result != null && ! typeof ( Attribute ). IsAssignableFrom ( result ))
{
result = null ;
}
}
return result ;
}
internal void SetTypeDefinition ( TypeDefinitionAst typeDefinitionAst )
{
Diagnostics . Assert ( _typeDefinitionAst == null , "Class definition is already set and cannot be changed" );
Diagnostics . Assert ( _type == null , "Cannot set class definition if type is already resolved" );
_typeDefinitionAst = typeDefinitionAst ;
}
/// <summary>
/// Simply return the <see cref="FullName"/> of the type.
/// </summary>
public override string ToString ()
{
return FullName ;
}
/// <summary/>
public override bool Equals ( object obj )
{
var other = obj as TypeName ;
if ( other == null )
return false ;
if (! _name . Equals ( other . _name , StringComparison . OrdinalIgnoreCase ))
return false ;
if ( AssemblyName == null )
return other . AssemblyName == null ;
if ( other . AssemblyName == null )
return false ;
return AssemblyName . Equals ( other . AssemblyName , StringComparison . OrdinalIgnoreCase );
}
/// <summary/>
public override int GetHashCode ()
{
var stringComparer = StringComparer . OrdinalIgnoreCase ;
var nameHashCode = stringComparer . GetHashCode ( _name );
if ( AssemblyName == null )
return nameHashCode ;
return Utils . CombineHashCodes ( nameHashCode , stringComparer . GetHashCode ( AssemblyName ));
}
/// <summary>
/// Check if the type names a <see cref="System.Type"/>, false otherwise.
/// </summary>
/// <param name="type">The given <see cref="System.Type"/></param>
/// <returns>Returns true if the type names a <see cref="System.Type"/>, false otherwise.</returns>
/// <remarks>
/// This helper function is now used to check 'Void' type only;
/// Other types may not work, for example, 'int'
/// </remarks>
internal bool IsType ( Type type )
{
string fullTypeName = type . FullName ;
if ( fullTypeName . Equals ( Name , StringComparison . OrdinalIgnoreCase ))
return true ;
int lastDotIndex = fullTypeName . LastIndexOf ( '.' );
if ( lastDotIndex >= 0 )
{
return fullTypeName . Substring ( lastDotIndex + 1 ). Equals ( Name , StringComparison . OrdinalIgnoreCase );
}
return false ;
}
Type ISupportsTypeCaching . CachedType
{
get { return _type ; }
set { _type = value ; }
}
}
/// <summary>
/// Represent a closed generic type including its arguments.
/// </summary>
public sealed class GenericTypeName : ITypeName , ISupportsTypeCaching
{
private string _cachedFullName ;
private Type _cachedType ;
private readonly IScriptExtent _extent ;
/// <summary>
/// Construct a generic type name.
/// </summary>
/// <param name="extent">The extent of the generic typename.</param>
/// <param name="genericTypeName">
/// The name of the generic class. The name does not need to include the backtick and number of expected arguments,
/// (e.g. <c>System.Collections.Generic.Dictionary`2</c>, but the backtick and number be included.
/// </param>
/// <param name="genericArguments">
/// The list of typenames that represent the arguments to the generic type named by <paramref name="genericTypeName"/>.
/// </param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="genericTypeName"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="genericArguments"/> is null or if <paramref name="genericArguments"/> is an empty collection.
/// </exception>
public GenericTypeName ( IScriptExtent extent , ITypeName genericTypeName , IEnumerable < ITypeName > genericArguments )
{
if ( genericTypeName == null || extent == null )
{
throw PSTraceSource . NewArgumentNullException ( extent == null ? "extent" : "genericTypeName" );
}
if ( genericArguments == null )
{
throw PSTraceSource . NewArgumentException ( "genericArguments" );
}
this . _extent = extent ;
this . TypeName = genericTypeName ;
this . GenericArguments = new ReadOnlyCollection < ITypeName >( genericArguments . ToArray ());
if ( this . GenericArguments . Count == 0 )
{
throw PSTraceSource . NewArgumentException ( "genericArguments" );
}
}
/// <summary>
/// Return the typename, using PowerShell syntax for generic type arguments.
/// </summary>
public string FullName
{
get
{
if ( _cachedFullName == null )
{
StringBuilder sb = new StringBuilder ();
sb . Append ( TypeName . Name );
sb . Append ( '[' );
bool first = true ;
for ( int index = 0 ; index < GenericArguments . Count ; index ++)
{
ITypeName typename = GenericArguments [ index ];
if (! first )
{
sb . Append ( ',' );
}
first = false ;
sb . Append ( typename . FullName );
}
sb . Append ( ']' );
var assemblyName = TypeName . AssemblyName ;
if ( assemblyName != null )
{
sb . Append ( ',' );
sb . Append ( assemblyName );
}
Interlocked . CompareExchange ( ref _cachedFullName , sb . ToString (), null );
}
return _cachedFullName ;
}
}
/// <summary>
/// The name of the type, including any namespace, but not including the assembly name, using PowerShell syntax for generic type arguments.
/// </summary>
public string Name
{
get
{
StringBuilder sb = new StringBuilder ();
sb . Append ( TypeName . Name );
sb . Append ( '[' );
bool first = true ;
for ( int index = 0 ; index < GenericArguments . Count ; index ++)
{
ITypeName typename = GenericArguments [ index ];
if (! first )
{
sb . Append ( ',' );
}
first = false ;
sb . Append ( typename . Name );
}
sb . Append ( ']' );
return sb . ToString ();
}
}
/// <summary>
/// The name of the assembly, if specified, otherwise null.
/// </summary>
public string AssemblyName { get { return TypeName . AssemblyName ; } }
/// <summary>
/// Always returns false because this class does not represent arrays.
/// </summary>
public bool IsArray { get { return false ; } }
/// <summary>
/// Always returns true because this class represents generics.
/// </summary>
public bool IsGeneric { get { return true ; } }
/// <summary>
/// The typename that specifies the generic class.
/// </summary>
public ITypeName TypeName { get ; private set ; }
/// <summary>
/// The generic arguments for this typename.
/// </summary>
public ReadOnlyCollection < ITypeName > GenericArguments { get ; private set ; }
/// <summary>
/// The extent of the typename.
/// </summary>
public IScriptExtent Extent { get { return _extent ; } }
/// <summary>
/// Returns the <see cref="System.Type"/> that this typename represents, if such a type exists, null otherwise.
/// </summary>
public Type GetReflectionType ()
{
if ( _cachedType == null )
{
Type generic = GetGenericType ( TypeName . GetReflectionType ());
if ( generic != null && generic . GetTypeInfo (). ContainsGenericParameters )
{
var argumentList = new List < Type >();
foreach ( var arg in GenericArguments )
{
var type = arg . GetReflectionType ();
if ( type == null )
return null ;
argumentList . Add ( type );
}
try
{
var type = generic . MakeGenericType ( argumentList . ToArray ());
try
{
// We don't need the TypeHandle, but it's a good indication that a type doesn't
// support reflection which we need for many things. So if this throws, we
// won't cache the type. This situation arises while defining a type that
// refers to itself somehow (e.g. returns an instance of this type, array of
// this type, or this type as a generic parameter.)
// By not caching, we'll eventually get the real reflection capable type after
// the type is fully defined.
var unused = type . TypeHandle ;
}
catch ( NotSupportedException )
{
return type ;
}
Interlocked . CompareExchange ( ref _cachedType , type , null );
}
catch ( Exception e )
{
CommandProcessorBase . CheckForSevereException ( e );
// We don't want to throw exception from GetReflectionType
}
}
}
return _cachedType ;
}
/// <summary>
/// Get the actual generic type if it's necessary
/// </summary>
/// <param name="generic"></param>
/// <returns></returns>
internal Type GetGenericType ( Type generic )
{
if ( generic == null || ! generic . GetTypeInfo (). ContainsGenericParameters )
{
if ( TypeName . FullName . IndexOf ( "`" , StringComparison . OrdinalIgnoreCase ) == - 1 )
{
var newTypeName = new TypeName ( _extent ,
string . Format ( CultureInfo . InvariantCulture , "{0}`{1}" , TypeName . FullName , GenericArguments . Count ));
generic = newTypeName . GetReflectionType ();
}
}
return generic ;
}
/// <summary>
/// Returns the <see cref="Type"/> this type represents, assuming the type is an attribute. The suffix
/// "Attribute" may be appended, if necessary, to resolve the type.
/// </summary>
/// <returns>
/// The <see cref="Type"/> if possible, null otherwise. Null may be returned for valid typenames if the assembly
/// containing the type has not been loaded.
/// </returns>
public Type GetReflectionAttributeType ()
{
Type type = GetReflectionType ();
if ( type == null )
{
Type generic = TypeName . GetReflectionAttributeType ();
TypeInfo genericTypeInfo = ( generic != null ) ? generic . GetTypeInfo () : null ;
if ( genericTypeInfo == null || ! genericTypeInfo . ContainsGenericParameters )
{
if ( TypeName . FullName . IndexOf ( "`" , StringComparison . OrdinalIgnoreCase ) == - 1 )
{
var newTypeName = new TypeName ( _extent ,
string . Format ( CultureInfo . InvariantCulture , "{0}Attribute`{1}" , TypeName . FullName , GenericArguments . Count ));
generic = newTypeName . GetReflectionType ();
genericTypeInfo = ( generic != null ) ? generic . GetTypeInfo () : null ;
}
}
if ( genericTypeInfo != null && genericTypeInfo . ContainsGenericParameters )
{
type = genericTypeInfo . MakeGenericType (( from arg in GenericArguments select arg . GetReflectionType ()). ToArray ());
Interlocked . CompareExchange ( ref _cachedType , type , null );
}
}
return type ;
}
/// <summary>
/// Simply return the <see cref="FullName"/> of the type.
/// </summary>
public override string ToString ()
{
return FullName ;
}
/// <summary/>
public override bool Equals ( object obj )
{
var other = obj as GenericTypeName ;
if ( other == null )
return false ;
if (! TypeName . Equals ( other . TypeName ))
return false ;
if ( GenericArguments . Count != other . GenericArguments . Count )
return false ;
var count = GenericArguments . Count ;
for ( int i = 0 ; i < count ; i ++)
{
if (! GenericArguments [ i ]. Equals ( other . GenericArguments [ i ]))
return false ;
}
return true ;
}
/// <summary/>
public override int GetHashCode ()
{
int hash = TypeName . GetHashCode ();
var count = GenericArguments . Count ;
for ( int i = 0 ; i < count ; i ++)
{
hash = Utils . CombineHashCodes ( hash , GenericArguments [ i ]. GetHashCode ());
}
return hash ;
}
Type ISupportsTypeCaching . CachedType
{
get { return _cachedType ; }
set { _cachedType = value ; }
}
}
/// <summary>
/// Represents the name of an array type including the dimensions.
/// </summary>
public sealed class ArrayTypeName : ITypeName , ISupportsTypeCaching
{
private string _cachedFullName ;
private Type _cachedType ;
private readonly IScriptExtent _extent ;
/// <summary>
/// Construct an ArrayTypeName.
/// </summary>
/// <param name="extent">The extent of the array typename.</param>
/// <param name="elementType">The name of the element type.</param>
/// <param name="rank">The number of dimensions in the array.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="elementType"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="rank"/> is 0 or negative.
/// </exception>
public ArrayTypeName ( IScriptExtent extent , ITypeName elementType , int rank )
{
if ( extent == null || elementType == null )
{
throw PSTraceSource . NewArgumentNullException ( extent == null ? "extent" : "name" );
}
if ( rank <= 0 )
{
throw PSTraceSource . NewArgumentException ( "rank" );
}
this . _extent = extent ;
this . Rank = rank ;
this . ElementType = elementType ;
}
private string GetName ( bool includeAssemblyName )
{
StringBuilder sb = new StringBuilder ();
try
{
RuntimeHelpers . EnsureSufficientExecutionStack ();
sb . Append ( ElementType . Name );
sb . Append ( '[' );
if ( Rank > 1 )
{
sb . Append ( ',' , Rank - 1 );
}
sb . Append ( ']' );
if ( includeAssemblyName )
{
var assemblyName = ElementType . AssemblyName ;
if ( assemblyName != null )
{
sb . Append ( ',' );
sb . Append ( assemblyName );
}
}
}
catch ( InsufficientExecutionStackException )
{
throw new ScriptCallDepthException ();
}
return sb . ToString ();
}
/// <summary>
/// Return the typename, using PowerShell syntax for the array dimensions.
/// </summary>
public string FullName
{
get
{
if ( _cachedFullName == null )
{
Interlocked . CompareExchange ( ref _cachedFullName , GetName ( includeAssemblyName : true ), null );
}
return _cachedFullName ;
}
}
/// <summary>
/// The name of the type, including any namespace, but not including the assembly name, using PowerShell syntax for the array dimensions.
/// </summary>
public string Name
{
get { return GetName ( includeAssemblyName : false ); }
}
/// <summary>
/// The name of the assembly, if specified, otherwise null.
/// </summary>
public string AssemblyName { get { return ElementType . AssemblyName ; } }
/// <summary>
/// Returns true always as this class represents arrays.
/// </summary>
public bool IsArray { get { return true ; } }
/// <summary>
/// Returns false always as this class never represents generics.
/// </summary>
public bool IsGeneric { get { return false ; } }
/// <summary>
/// The element type of the array.
/// </summary>
public ITypeName ElementType { get ; private set ; }
/// <summary>
/// The rank of the array.
/// </summary>
public int Rank { get ; private set ; }
/// <summary>
/// The extent of the typename.
/// </summary>
public IScriptExtent Extent { get { return _extent ; } }
/// <summary>
/// Returns the <see cref="System.Type"/> that this typename represents, if such a type exists, null otherwise.
/// </summary>
public Type GetReflectionType ()
{
try
{
RuntimeHelpers . EnsureSufficientExecutionStack ();
if ( _cachedType == null )
{
Type elementType = ElementType . GetReflectionType ();
if ( elementType != null )
{
Type type = Rank == 1 ? elementType . MakeArrayType () : elementType . MakeArrayType ( Rank );
try
{
// We don't need the TypeHandle, but it's a good indication that a type doesn't
// support reflection which we need for many things. So if this throws, we
// won't cache the type. This situation arises while defining a type that
// refers to itself somehow (e.g. returns an instance of this type, array of
// this type, or this type as a generic parameter.)
// By not caching, we'll eventually get the real reflection capable type after
// the type is fully defined.
var unused = type . TypeHandle ;
}
catch ( NotSupportedException )
{
return type ;
}
Interlocked . CompareExchange ( ref _cachedType , type , null );
}
}
}
catch ( InsufficientExecutionStackException )
{
throw new ScriptCallDepthException ();
}
return _cachedType ;
}
/// <summary>
/// Always return null, arrays can never be an attribute.
/// </summary>
public Type GetReflectionAttributeType ()
{
return null ;
}
/// <summary>
/// Simply return the <see cref="FullName"/> of the type.
/// </summary>
public override string ToString ()
{
return FullName ;
}
/// <summary/>
public override bool Equals ( object obj )
{
var other = obj as ArrayTypeName ;
if ( other == null )
return false ;
return ElementType . Equals ( other . ElementType ) && Rank == other . Rank ;
}
/// <summary/>
public override int GetHashCode ()
{
return Utils . CombineHashCodes ( ElementType . GetHashCode (), Rank . GetHashCode ());
}
Type ISupportsTypeCaching . CachedType
{
get { return _cachedType ; }
set { _cachedType = value ; }
}
}
/// <summary>
/// A class that allows a <see cref="System.Type"/> to be used directly in the PowerShell ast.
/// </summary>
public sealed class ReflectionTypeName : ITypeName , ISupportsTypeCaching
{
private readonly Type _type ;
/// <summary>
/// Construct a typename from a <see cref="System.Type"/>.
/// </summary>
/// <param name="type">The type to wrap.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="type"/> is null.
/// </exception>
public ReflectionTypeName ( Type type )
{
if ( type == null )
{
throw PSTraceSource . NewArgumentNullException ( "type" );
}
this . _type = type ;
}
/// <summary>
/// Returns the typename in PowerShell syntax.
/// </summary>
public string FullName { get { return ToStringCodeMethods . Type ( _type ); } }
/// <summary>
/// Returns the typename in PowerShell syntax.
/// </summary>
public string Name { get { return FullName ; } }
/// <summary>
/// The name of the assembly.
/// </summary>
public string AssemblyName { get { return _type . GetTypeInfo (). Assembly . FullName ; } }
/// <summary>
/// Returns true if the type is an array, false otherwise.
/// </summary>
public bool IsArray { get { return _type . IsArray ; } }
/// <summary>
/// Returns true if the type is a generic, false otherwise.
/// </summary>
public bool IsGeneric { get { return _type . GetTypeInfo (). IsGenericType ; } }
/// <summary>
/// The extent of the typename.
/// </summary>
public IScriptExtent Extent { get { return PositionUtilities . EmptyExtent ; } }
/// <summary>
/// Returns the <see cref="System.Type"/> for this typename. Never returns null.
/// </summary>
public Type GetReflectionType ()
{
return _type ;
}
/// <summary>
/// Assuming the typename is an attribute, returns the <see cref="System.Type"/> that this typename represents.
/// </summary>
public Type GetReflectionAttributeType ()
{
return _type ;
}
/// <summary>
/// Simply return the <see cref="FullName"/> of the type.
/// </summary>
public override string ToString ()
{
return FullName ;
}
/// <summary/>
public override bool Equals ( object obj )
{
var other = obj as ReflectionTypeName ;
if ( other == null )
return false ;
return _type == other . _type ;
}
/// <summary/>
public override int GetHashCode ()
{
return _type . GetHashCode ();
}
Type ISupportsTypeCaching . CachedType
{
get { return _type ; }
set { throw new InvalidOperationException (); }
}
}
/// <summary>
/// The ast that represents a type literal expression, e.g. <c>[int]</c>.
/// </summary>
public class TypeExpressionAst : ExpressionAst
{
/// <summary>
/// Construct a type literal expression.
/// </summary>
/// <param name="extent">The extent of the typename, including the opening and closing square braces.</param>
/// <param name="typeName">The typename for the constructed ast.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="typeName"/> is null.
/// </exception>
public TypeExpressionAst ( IScriptExtent extent , ITypeName typeName )
: base ( extent )
{
if ( typeName == null )
{
throw PSTraceSource . NewArgumentNullException ( "typeName" );
}
this . TypeName = typeName ;
}
/// <summary>
/// The name of the type. This property is never null.
/// </summary>
public ITypeName TypeName { get ; private set ; }
/// <summary>
/// Copy the TypeExpressionAst instance
/// </summary>
public override Ast Copy ()
{
return new TypeExpressionAst ( this . Extent , this . TypeName );
}
/// <summary>
/// The static type of a type literal is always <c>typeof(Type)</c>.
/// </summary>
public override Type StaticType { get { return typeof ( Type ); } }
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
yield return new PSTypeName ( StaticType );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitTypeExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitTypeExpression ( this );
return visitor . CheckForPostAction ( this , ( action == AstVisitAction . SkipChildren ? AstVisitAction . Continue : action ));
}
#endregion Visitors
}
/// <summary>
/// The ast representing a variable reference, either normal references, e.g. <c>$true</c>, or splatted references
/// <c>@PSBoundParameters</c>.
/// </summary>
public class VariableExpressionAst : ExpressionAst , ISupportsAssignment , IAssignableValue
{
/// <summary>
/// Construct a variable reference.
/// </summary>
/// <param name="extent">The extent of the variable.</param>
/// <param name="variableName">
/// The name of the variable. A leading '$' or '@' is not removed, those characters are assumed to be part of
/// the variable name.
/// </param>
/// <param name="splatted">True if splatting, like <c>@PSBoundParameters</c>, false otherwise, like <c>$false</c>.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="variableName"/> is null, or if <paramref name="variableName"/>
/// is an empty string.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
public VariableExpressionAst ( IScriptExtent extent , string variableName , bool splatted )
: base ( extent )
{
if ( string . IsNullOrEmpty ( variableName ))
{
throw PSTraceSource . NewArgumentNullException ( "variableName" );
}
this . VariablePath = new VariablePath ( variableName );
this . Splatted = splatted ;
}
/// <summary>
/// Construct a variable reference from a token. Used from the parser.
/// </summary>
internal VariableExpressionAst ( VariableToken token )
: this ( token . Extent , token . VariablePath , ( token . Kind == TokenKind . SplattedVariable ))
{
}
/// <summary>
/// Construct a variable reference with an exising VariablePath (rather than construct a new one.)
/// </summary>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="variablePath"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
public VariableExpressionAst ( IScriptExtent extent , VariablePath variablePath , bool splatted )
: base ( extent )
{
if ( variablePath == null )
{
throw PSTraceSource . NewArgumentNullException ( "variablePath" );
}
this . VariablePath = variablePath ;
this . Splatted = splatted ;
}
/// <summary>
/// The name of the variable. This property is never null.
/// </summary>
public VariablePath VariablePath { get ; private set ; }
/// <summary>
/// True if splatting syntax was used, false otherwise.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
public bool Splatted { get ; private set ; }
/// <summary>
/// Check if the variable is one of $true, $false and $null
/// </summary>
/// <returns>
/// True if it is a constant variable
/// </returns>
public bool IsConstantVariable ()
{
if ( this . VariablePath . IsVariable )
{
string name = this . VariablePath . UnqualifiedPath ;
if ( name . Equals ( SpecialVariables . True , StringComparison . OrdinalIgnoreCase ) ||
name . Equals ( SpecialVariables . False , StringComparison . OrdinalIgnoreCase ) ||
name . Equals ( SpecialVariables . Null , StringComparison . OrdinalIgnoreCase ))
{
return true ;
}
}
return false ;
}
/// <summary>
/// Copy the VariableExpressionAst instance
/// </summary>
public override Ast Copy ()
{
return new VariableExpressionAst ( this . Extent , this . VariablePath , this . Splatted );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
// We don't need to handle drive qualified variables, we can usually get those values
// without needing to "guess" at the type.
if (! VariablePath . IsVariable )
{
// Not a variable - the caller should have already tried going to session state
// to get the item and hence it's type, but that must have failed. Don't try again.
yield break ;
}
Ast parent = this . Parent ;
if ( VariablePath . IsUnqualified &&
( SpecialVariables . IsUnderbar ( VariablePath . UserPath )
|| VariablePath . UserPath . Equals ( SpecialVariables . PSItem , StringComparison . OrdinalIgnoreCase )))
{
// $_ is special, see if we're used in a script block in some pipeline.
while ( parent != null )
{
if ( parent is ScriptBlockExpressionAst )
break ;
parent = parent . Parent ;
}
if ( parent != null )
{
if ( parent . Parent is CommandExpressionAst && parent . Parent . Parent is PipelineAst )
{
// Script block in a hash table, could be something like:
// dir | ft @{ Expression = { $_ } }
if ( parent . Parent . Parent . Parent is HashtableAst )
{
parent = parent . Parent . Parent . Parent ;
}
else if ( parent . Parent . Parent . Parent is ArrayLiteralAst && parent . Parent . Parent . Parent . Parent is HashtableAst )
{
parent = parent . Parent . Parent . Parent . Parent ;
}
}
if ( parent . Parent is CommandParameterAst )
{
parent = parent . Parent ;
}
var commandAst = parent . Parent as CommandAst ;
if ( commandAst != null )
{
// We found a command, see if there is a previous command in the pipeline.
PipelineAst pipelineAst = ( PipelineAst ) commandAst . Parent ;
var previousCommandIndex = pipelineAst . PipelineElements . IndexOf ( commandAst ) - 1 ;
if ( previousCommandIndex >= 0 )
{
foreach ( var result in pipelineAst . PipelineElements [ 0 ]. GetInferredType ( context ))
{
if ( result . Type != null )
{
// Assume (because we're looking at $_ and we're inside a script block that is an
// argument to some command) that the type we're getting is actually unrolled.
// This might not be right in all cases, but with our simple analysis, it'r
// right more often than it's wrong.
if ( result . Type . IsArray )
{
yield return new PSTypeName ( result . Type . GetElementType ());
continue ;
}
if ( typeof ( IEnumerable ). IsAssignableFrom ( result . Type ))
{
// We can't deduce much from IEnumerable, but we can if it's generic.
var enumerableInterfaces = result . Type . GetInterfaces (). Where (
t =>
t . GetTypeInfo (). IsGenericType &&
t . GetGenericTypeDefinition () == typeof ( IEnumerable <>));
foreach ( var i in enumerableInterfaces )
{
yield return new PSTypeName ( i . GetGenericArguments ()[ 0 ]);
}
continue ;
}
}
yield return result ;
}
}
yield break ;
}
}
}
// For certain variables, we always know their type, well at least we can assume we know.
if ( VariablePath . IsUnqualified )
{
if ( VariablePath . UserPath . Equals ( SpecialVariables . This , StringComparison . OrdinalIgnoreCase ) &&
context . CurrentTypeDefinitionAst != null )
{
yield return new PSTypeName ( context . CurrentTypeDefinitionAst );
yield break ;
}
for ( int i = 0 ; i < SpecialVariables . AutomaticVariables . Length ; i ++)
{
if ( VariablePath . UserPath . Equals ( SpecialVariables . AutomaticVariables [ i ], StringComparison . OrdinalIgnoreCase ))
{
var type = SpecialVariables . AutomaticVariableTypes [ i ];
if (!( type == typeof ( object )))
yield return new PSTypeName ( type );
break ;
}
}
}
// Look for our variable as a parameter or on the lhs of an assignment - hopefully we'll find either
// a type constraint or at least we can use the rhs to infer the type.
while ( parent . Parent != null )
{
parent = parent . Parent ;
}
if ( parent . Parent is FunctionDefinitionAst )
{
parent = parent . Parent ;
}
int startOffset = this . Extent . StartOffset ;
var targetAsts = AstSearcher . FindAll ( parent ,
ast => ( ast is ParameterAst || ast is AssignmentStatementAst || ast is ForEachStatementAst || ast is CommandAst )
&& AstAssignsToSameVariable ( ast )
&& ast . Extent . EndOffset < startOffset ,
searchNestedScriptBlocks : true );
var parameterAst = targetAsts . OfType < ParameterAst >(). FirstOrDefault ();
if ( parameterAst != null )
{
var parameterTypes = parameterAst . GetInferredType ( context ). ToArray ();
if ( parameterTypes . Length > 0 )
{
foreach ( var parameterType in parameterTypes )
{
yield return parameterType ;
}
yield break ;
}
}
var assignAsts = targetAsts . OfType < AssignmentStatementAst >(). ToArray ();
// If any of the assignments lhs use a type constraint, then we use that.
// Otherwise, we use the rhs of the "nearest" assignment
foreach ( var assignAst in assignAsts )
{
var lhsConvert = assignAst . Left as ConvertExpressionAst ;
if ( lhsConvert != null )
{
yield return new PSTypeName ( lhsConvert . Type . TypeName );
yield break ;
}
}
var foreachAst = targetAsts . OfType < ForEachStatementAst >(). FirstOrDefault ();
if ( foreachAst != null )
{
foreach ( var typeName in foreachAst . Condition . GetInferredType ( context ))
{
yield return typeName ;
}
yield break ;
}
var commandCompletionAst = targetAsts . OfType < CommandAst >(). FirstOrDefault ();
if ( commandCompletionAst != null )
{
foreach ( var typeName in commandCompletionAst . GetInferredType ( context ))
{
yield return typeName ;
}
yield break ;
}
int smallestDiff = int . MaxValue ;
AssignmentStatementAst closestAssignment = null ;
foreach ( var assignAst in assignAsts )
{
var endOffset = assignAst . Extent . EndOffset ;
if (( startOffset - endOffset ) < smallestDiff )
{
smallestDiff = startOffset - endOffset ;
closestAssignment = assignAst ;
}
}
if ( closestAssignment != null )
{
foreach ( var type in closestAssignment . Right . GetInferredType ( context ))
{
yield return type ;
}
}
}
internal bool IsSafeVariableReference ( HashSet < string > validVariables , ref bool usesParameter )
{
bool ok = false ;
if ( this . VariablePath . IsAnyLocal ())
{
var varName = this . VariablePath . UnqualifiedPath ;
if ((( validVariables != null ) && validVariables . Contains ( varName )) ||
varName . Equals ( SpecialVariables . Args , StringComparison . OrdinalIgnoreCase ))
{
ok = true ;
usesParameter = true ;
}
else
{
ok = ! this . Splatted
&& this . IsConstantVariable ();
}
}
return ok ;
}
private bool AstAssignsToSameVariable ( Ast ast )
{
var parameterAst = ast as ParameterAst ;
if ( parameterAst != null )
{
return VariablePath . IsUnscopedVariable &&
parameterAst . Name . VariablePath . UnqualifiedPath . Equals ( VariablePath . UnqualifiedPath , StringComparison . OrdinalIgnoreCase );
}
var foreachAst = ast as ForEachStatementAst ;
if ( foreachAst != null )
{
return VariablePath . IsUnscopedVariable &&
foreachAst . Variable . VariablePath . UnqualifiedPath . Equals ( VariablePath . UnqualifiedPath , StringComparison . OrdinalIgnoreCase );
}
var commandAst = ast as CommandAst ;
if ( commandAst != null )
{
string [] variableParameters = new string [] { "PV" , "PipelineVariable" , "OV" , "OutVariable" };
StaticBindingResult bindingResult = StaticParameterBinder . BindCommand ( commandAst , false , variableParameters );
if ( bindingResult != null )
{
ParameterBindingResult parameterBindingResult ;
foreach ( string commandVariableParameter in variableParameters )
{
if ( bindingResult . BoundParameters . TryGetValue ( commandVariableParameter , out parameterBindingResult ))
{
if ( String . Equals ( VariablePath . UnqualifiedPath , ( string ) parameterBindingResult . ConstantValue , StringComparison . OrdinalIgnoreCase ))
{
return true ;
}
}
}
}
return false ;
}
var assignmentAst = ( AssignmentStatementAst ) ast ;
var lhs = assignmentAst . Left ;
var convertExpr = lhs as ConvertExpressionAst ;
if ( convertExpr != null )
{
lhs = convertExpr . Child ;
}
var varExpr = lhs as VariableExpressionAst ;
if ( varExpr == null )
return false ;
var candidateVarPath = varExpr . VariablePath ;
if ( candidateVarPath . UserPath . Equals ( VariablePath . UserPath , StringComparison . OrdinalIgnoreCase ))
return true ;
// The following condition is making an assumption that at script scope, we didn't use $script:, but in the local scope, we did
// If we are searching anything other than script scope, this is wrong.
if ( VariablePath . IsScript && VariablePath . UnqualifiedPath . Equals ( candidateVarPath . UnqualifiedPath , StringComparison . OrdinalIgnoreCase ))
return true ;
return false ;
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitVariableExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitVariableExpression ( this );
return visitor . CheckForPostAction ( this , ( action == AstVisitAction . SkipChildren ? AstVisitAction . Continue : action ));
}
#endregion Visitors
#region Code Generation Details
private int _tupleIndex = VariableAnalysis . Unanalyzed ;
internal int TupleIndex
{
get { return _tupleIndex ; }
set { _tupleIndex = value ; }
}
internal bool Automatic { get ; set ; }
internal bool Assigned { get ; set ; }
IAssignableValue ISupportsAssignment . GetAssignableValue ()
{
return this ;
}
Expression IAssignableValue . GetValue ( Compiler compiler , List < Expression > exprs , List < ParameterExpression > temps )
{
return ( Expression ) compiler . VisitVariableExpression ( this );
}
Expression IAssignableValue . SetValue ( Compiler compiler , Expression rhs )
{
if ( this . VariablePath . IsVariable && this . VariablePath . UnqualifiedPath . Equals ( SpecialVariables . Null , StringComparison . OrdinalIgnoreCase ))
{
return rhs ;
}
IEnumerable < PropertyInfo > tupleAccessPath ;
bool localInTuple ;
Type targetType = GetVariableType ( compiler , out tupleAccessPath , out localInTuple );
// Value types must be copied on assignment (if they are mutable), boxed or not. This preserves language
// semantics from V1/V2, and might be slightly more natural for a dynamic language.
// To generate good code, we assume any object or PSObject could be a boxed value type, and generate a dynamic
// site to handle copying as necessary. Given this assumption, here are the possibilities:
//
// - rhs is reference type
// * just convert to lhs type
// - rhs is value type
// * lhs type is value type, copy made by simple assignment
// * lhs type is object, copy is made by boxing, also handled in simple assignment
// - rhs is boxed value type
// * lhs type is value type, copy is made by unboxing conversion
// * lhs type is object/psobject, copy must be made dynamically (boxed value type can't be known statically)
var rhsType = rhs . Type ;
if ( localInTuple &&
( targetType == typeof ( object ) || targetType == typeof ( PSObject )) &&
( rhsType == typeof ( object ) || rhsType == typeof ( PSObject )))
{
rhs = DynamicExpression . Dynamic ( PSVariableAssignmentBinder . Get (), typeof ( object ), rhs );
}
rhs = rhs . Convert ( targetType );
if (! localInTuple )
{
return Compiler . CallSetVariable ( Expression . Constant ( VariablePath ), rhs );
}
Expression lhs = compiler . LocalVariablesParameter ;
foreach ( var property in tupleAccessPath )
{
lhs = Expression . Property ( lhs , property );
}
return Expression . Assign ( lhs , rhs );
}
internal Type GetVariableType ( Compiler compiler , out IEnumerable < PropertyInfo > tupleAccessPath , out bool localInTuple )
{
Type targetType = null ;
localInTuple = _tupleIndex >= 0 &&
( compiler . Optimize || _tupleIndex < ( int ) AutomaticVariable . NumberOfAutomaticVariables );
tupleAccessPath = null ;
if ( localInTuple )
{
tupleAccessPath = MutableTuple . GetAccessPath ( compiler . LocalVariablesTupleType , _tupleIndex );
targetType = tupleAccessPath . Last (). PropertyType ;
}
else
{
targetType = typeof ( object );
}
return targetType ;
}
#endregion Code Generation Details
}
/// <summary>
/// The ast representing constant values, such as numbers. Constant values mean truly constant, as in, the value is
/// always the same. Expandable strings with variable references (e.g. <c>"$val"</c>) or sub-expressions
/// (e.g. <c>"$(1)"</c>) are not considered constant.
/// </summary>
public class ConstantExpressionAst : ExpressionAst
{
/// <summary>
/// Construct a constant expression.
/// </summary>
/// <param name="extent">The extent of the constant.</param>
/// <param name="value">The value of the constant.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
public ConstantExpressionAst ( IScriptExtent extent , object value )
: base ( extent )
{
this . Value = value ;
}
internal ConstantExpressionAst ( NumberToken token )
: base ( token . Extent )
{
this . Value = token . Value ;
}
/// <summary>
/// The value of the constant. This property is null only if the expression represents the null constant.
/// </summary>
public object Value { get ; private set ; }
/// <summary>
/// Copy the ConstantExpressionAst instance
/// </summary>
public override Ast Copy ()
{
return new ConstantExpressionAst ( this . Extent , this . Value );
}
/// <summary>
/// The static type of a constant is whatever type the value is, or if null, then assume it's <c>typeof(object)</c>.
/// </summary>
public override Type StaticType
{
get { return Value != null ? Value . GetType () : typeof ( object ); }
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
if ( Value != null )
{
yield return new PSTypeName ( Value . GetType ());
}
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitConstantExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitConstantExpression ( this );
return visitor . CheckForPostAction ( this , ( action == AstVisitAction . SkipChildren ? AstVisitAction . Continue : action ));
}
#endregion Visitors
}
/// <summary>
/// The kind of string constant.
/// </summary>
public enum StringConstantType
{
/// <summary>
/// A string enclosed in single quotes, e.g. <c>'some text'</c>.
/// </summary>
SingleQuoted ,
/// <summary>
/// A here string enclosed in single quotes, e.g. <c> @'
/// a here string
/// '@
/// </c>
/// </summary>
SingleQuotedHereString ,
/// <summary>
/// A string enclosed in double quotes, e.g. <c>"some text"</c>.
/// </summary>
DoubleQuoted ,
/// <summary>
/// A here string enclosed in double quotes, e.g. <c> @"
/// a here string
/// "@
/// </c>
/// </summary>
DoubleQuotedHereString ,
/// <summary>
/// A string like token not enclosed in any quotes. This usually includes a command name or command argument.
/// </summary>
BareWord
}
/// <summary>
/// The ast that represents a constant string expression that is always constant. This includes both single and
/// double quoted strings, but the double quoted strings will not be scanned for variable references and sub-expressions.
/// If expansion of the string is required, use <see cref="ExpandableStringExpressionAst"/>.
/// </summary>
public class StringConstantExpressionAst : ConstantExpressionAst
{
/// <summary>
/// Construct a string constant expression.
/// </summary>
/// <param name="extent">The extent of the string constant, including quotes.</param>
/// <param name="value">The value of the string.</param>
/// <param name="stringConstantType">The type of string.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="value"/> is null.
/// </exception>
public StringConstantExpressionAst ( IScriptExtent extent , string value , StringConstantType stringConstantType )
: base ( extent , value )
{
if ( value == null )
{
throw PSTraceSource . NewArgumentNullException ( "value" );
}
this . StringConstantType = stringConstantType ;
}
internal StringConstantExpressionAst ( StringToken token )
: base ( token . Extent , token . Value )
{
this . StringConstantType = MapTokenKindToStringContantKind ( token );
}
/// <summary>
/// The type of string.
/// </summary>
public StringConstantType StringConstantType { get ; private set ; }
/// <summary>
/// The value of the string, not including the quotes used.
/// </summary>
public new string Value { get { return ( string ) base . Value ; } }
/// <summary>
/// Copy the StringConstantExpressionAst instance
/// </summary>
public override Ast Copy ()
{
return new StringConstantExpressionAst ( this . Extent , this . Value , this . StringConstantType );
}
/// <summary>
/// The type of a StringConstantExpressionAst is always <c>typeof(string)</c>.
/// </summary>
public override Type StaticType
{
get { return typeof ( string ); }
}
internal static StringConstantType MapTokenKindToStringContantKind ( Token token )
{
switch ( token . Kind )
{
case TokenKind . StringExpandable :
return StringConstantType . DoubleQuoted ;
case TokenKind . HereStringLiteral :
return StringConstantType . SingleQuotedHereString ;
case TokenKind . HereStringExpandable :
return StringConstantType . DoubleQuotedHereString ;
case TokenKind . StringLiteral :
return StringConstantType . SingleQuoted ;
case TokenKind . Generic :
return StringConstantType . BareWord ;
}
throw PSTraceSource . NewInvalidOperationException ();
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitStringConstantExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitStringConstantExpression ( this );
return visitor . CheckForPostAction ( this , ( action == AstVisitAction . SkipChildren ? AstVisitAction . Continue : action ));
}
#endregion Visitors
}
/// <summary>
/// The ast that repesents a double quoted string (here string or normal string) and can have nested variable
/// references or sub-expressions, e.g. <c>"Name: $name`nAge: $([DateTime]::Now.Year - $dob.Year)"</c>.
/// </summary>
public class ExpandableStringExpressionAst : ExpressionAst
{
/// <summary>
/// Construct an expandable string. The value is scanned for nested variable references and expressions
/// which are evaluated at runtime when this ast is compiled.
/// </summary>
/// <param name="extent">The extent of the string.</param>
/// <param name="value">The unexpanded value of the string.</param>
/// <param name="type">The kind of string, must be one of<list>
/// <see cref="System.Management.Automation.Language.StringConstantType.DoubleQuoted"/>
/// <see cref="System.Management.Automation.Language.StringConstantType.DoubleQuotedHereString"/>
/// <see cref="System.Management.Automation.Language.StringConstantType.BareWord"/>
/// </list></param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="value"/> or <paramref name="extent"/> is null.
/// </exception>
public ExpandableStringExpressionAst ( IScriptExtent extent ,
string value ,
StringConstantType type )
: base ( extent )
{
if ( value == null )
{
throw PSTraceSource . NewArgumentNullException ( "value" );
}
if ( type != StringConstantType . DoubleQuoted && type != StringConstantType . DoubleQuotedHereString
&& type != StringConstantType . BareWord )
{
throw PSTraceSource . NewArgumentException ( "type" );
}
var ast = Language . Parser . ScanString ( value );
var expandableStringAst = ast as ExpandableStringExpressionAst ;
if ( expandableStringAst != null )
{
this . FormatExpression = expandableStringAst . FormatExpression ;
this . NestedExpressions = expandableStringAst . NestedExpressions ;
}
else
{
// We always compile to a format expression. In the rare case that some external code (this can't happen
// internally) passes in a string that doesn't require any expansion, we still need to generate code that
// works. This is slow as the ast is a string constant, but it should be rare.
this . FormatExpression = "{0}" ;
this . NestedExpressions = new ReadOnlyCollection < ExpressionAst >( new [] { ast });
}
// Set parents properly
for ( int i = 0 ; i < this . NestedExpressions . Count ; i ++)
{
this . NestedExpressions [ i ]. ClearParent ();
}
SetParents ( this . NestedExpressions );
this . Value = value ;
this . StringConstantType = type ;
}
/// <summary>
/// Construct an expandable string expression from a string token. Used from the parser after parsing
/// the nested tokens. This method is internal mainly so we can avoid validating <paramref name="formatString"/>.
/// </summary>
internal ExpandableStringExpressionAst ( Token token , string value , string formatString , IEnumerable < ExpressionAst > nestedExpressions )
: this ( token . Extent , value , formatString ,
StringConstantExpressionAst
. MapTokenKindToStringContantKind ( token ),
nestedExpressions )
{
}
private ExpandableStringExpressionAst ( IScriptExtent extent , string value , string formatString ,
StringConstantType type , IEnumerable < ExpressionAst > nestedExpressions )
: base ( extent )
{
Diagnostics . Assert ( nestedExpressions != null && nestedExpressions . Any (), "Must specify non-empty expressions." );
this . FormatExpression = formatString ;
this . Value = value ;
this . StringConstantType = type ;
this . NestedExpressions = new ReadOnlyCollection < ExpressionAst >( nestedExpressions . ToArray ());
SetParents ( NestedExpressions );
}
/// <summary>
/// The value of string, not including the quote characters and without any variables replaced.
/// This property is never null.
/// </summary>
public string Value { get ; private set ; }
/// <summary>
/// The type of string.
/// </summary>
public StringConstantType StringConstantType { get ; private set ; }
/// <summary>
/// A non-empty collection of expressions contained within the string. The nested expressions are always either
/// instances of <see cref="VariableExpressionAst"/> or <see cref="SubExpressionAst"/>.
/// </summary>
public ReadOnlyCollection < ExpressionAst > NestedExpressions { get ; private set ; }
/// <summary>
/// Copy the ExpandableStringExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newNestedExpressions = CopyElements ( this . NestedExpressions );
return new ExpandableStringExpressionAst ( this . Extent , this . Value , this . FormatExpression , this . StringConstantType , newNestedExpressions );
}
/// <summary>
/// The type of a StringConstantExpressionAst is always <c>typeof(string)</c>.
/// </summary>
public override Type StaticType
{
get { return typeof ( string ); }
}
/// <summary>
/// The format expression needed to execute this ast. It is generated by the scanner, it is not provided by clients.
/// </summary>
internal string FormatExpression { get ; private set ; }
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
yield return new PSTypeName ( typeof ( string ));
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitExpandableStringExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitExpandableStringExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue && NestedExpressions != null )
{
for ( int index = 0 ; index < NestedExpressions . Count ; index ++)
{
var exprAst = NestedExpressions [ index ];
action = exprAst . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents an anonymous script block expression, e.g. <c>{ dir }</c>.
/// </summary>
public class ScriptBlockExpressionAst : ExpressionAst
{
/// <summary>
/// Construct a script block expression.
/// </summary>
/// <param name="extent">The extent of the script block, from the opening curly brace to the closing curly brace.</param>
/// <param name="scriptBlock">The script block.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="scriptBlock"/> is null.
/// </exception>
public ScriptBlockExpressionAst ( IScriptExtent extent , ScriptBlockAst scriptBlock )
: base ( extent )
{
if ( scriptBlock == null )
{
throw PSTraceSource . NewArgumentNullException ( "scriptBlock" );
}
this . ScriptBlock = scriptBlock ;
SetParent ( scriptBlock );
}
/// <summary>
/// The ast for the scriptblock that this ast represent. This property is never null.
/// </summary>
public ScriptBlockAst ScriptBlock { get ; private set ; }
/// <summary>
/// Copy the ScriptBlockExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newScriptBlock = CopyElement ( this . ScriptBlock );
return new ScriptBlockExpressionAst ( this . Extent , newScriptBlock );
}
/// <summary>
/// The result of a <see cref="ScriptBlockExpressionAst"/> is always <c>typeof(<see cref="ScriptBlock"/></c>).
/// </summary>
public override Type StaticType
{
get { return typeof ( ScriptBlock ); }
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
yield return new PSTypeName ( typeof ( ScriptBlock ));
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitScriptBlockExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitScriptBlockExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = ScriptBlock . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents an array literal expression, e.g. <c>1,2,3</c>. An array expression, e.g. <c>@(dir)</c>,
/// is represented by <see cref="ArrayExpressionAst"/>. An array literal expression can be constructed from a single
/// element, as happens with the unary comma operator, e.g. <c>,4</c>.
/// </summary>
public class ArrayLiteralAst : ExpressionAst , ISupportsAssignment
{
/// <summary>
/// Construct an array literal expression.
/// </summary>
/// <param name="extent">The extent of all of the elements.</param>
/// <param name="elements">The collection of asts that represent the array literal.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="elements"/> is null or is an empty collection.
/// </exception>
public ArrayLiteralAst ( IScriptExtent extent , IList < ExpressionAst > elements )
: base ( extent )
{
if ( elements == null || ! elements . Any ())
{
throw PSTraceSource . NewArgumentException ( "elements" );
}
this . Elements = new ReadOnlyCollection < ExpressionAst >( elements );
SetParents ( Elements );
}
/// <summary>
/// The non-empty collection of asts of the elements of the array, or null if no elements were specified (e.g. <c>@()</c>).
/// </summary>
public ReadOnlyCollection < ExpressionAst > Elements { get ; private set ; }
/// <summary>
/// Copy the ArrayLiteralAst instance
/// </summary>
public override Ast Copy ()
{
var newElements = CopyElements ( this . Elements );
return new ArrayLiteralAst ( this . Extent , newElements );
}
/// <summary>
/// The result of an <see cref="ArrayLiteralAst"/> is always <c>typeof(object[])</c>.
/// </summary>
public override Type StaticType { get { return typeof ( object []); } }
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
yield return new PSTypeName ( typeof ( object []));
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitArrayLiteral ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitArrayLiteral ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < Elements . Count ; index ++)
{
var element = Elements [ index ];
action = element . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
IAssignableValue ISupportsAssignment . GetAssignableValue ()
{
return new ArrayAssignableValue { ArrayLiteral = this };
}
}
/// <summary>
/// The ast that represents a hash literal, e.g. <c>@{a = 1}</c>.
/// </summary>
public class HashtableAst : ExpressionAst
{
private static readonly ReadOnlyCollection < KeyValuePair > EmptyKeyValuePairs = Utils . EmptyReadOnlyCollection < KeyValuePair >();
/// <summary>
/// Construct a hash literal ast.
/// </summary>
/// <param name="extent">The extent of the literal, from '@{' to the closing '}'.</param>
/// <param name="keyValuePairs">The optionally null or empty list of key/value pairs.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> is null.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public HashtableAst ( IScriptExtent extent , IEnumerable < KeyValuePair > keyValuePairs )
: base ( extent )
{
if ( keyValuePairs != null )
{
this . KeyValuePairs = new ReadOnlyCollection < KeyValuePair >( keyValuePairs . ToArray ());
SetParents ( KeyValuePairs );
}
else
{
this . KeyValuePairs = EmptyKeyValuePairs ;
}
}
/// <summary>
/// The pairs of key names and asts for values used to construct the hash table.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public ReadOnlyCollection < KeyValuePair > KeyValuePairs { get ; private set ; }
/// <summary>
/// Copy the HashtableAst instance
/// </summary>
public override Ast Copy ()
{
List < KeyValuePair > newKeyValuePairs = null ;
if ( this . KeyValuePairs . Count > 0 )
{
newKeyValuePairs = new List < KeyValuePair >( this . KeyValuePairs . Count );
for ( int i = 0 ; i < this . KeyValuePairs . Count ; i ++)
{
var keyValuePair = this . KeyValuePairs [ i ];
var newKey = CopyElement ( keyValuePair . Item1 );
var newValue = CopyElement ( keyValuePair . Item2 );
newKeyValuePairs . Add ( Tuple . Create ( newKey , newValue ));
}
}
return new HashtableAst ( this . Extent , newKeyValuePairs );
}
/// <summary>
/// The result type of a <see cref="HashtableAst"/> is always <c>typeof(<see cref="Hashtable"/>)</c>.
/// </summary>
public override Type StaticType { get { return typeof ( Hashtable ); } }
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
yield return new PSTypeName ( typeof ( Hashtable ));
}
// Indicates that this ast was consructed as part of a schematized object instead of just a plain hash literal.
internal bool IsSchemaElement { get ; set ; }
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitHashtable ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitHashtable ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
{
for ( int index = 0 ; index < KeyValuePairs . Count ; index ++)
{
var keyValuePairAst = KeyValuePairs [ index ];
action = keyValuePairAst . Item1 . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
action = keyValuePairAst . Item2 . InternalVisit ( visitor );
if ( action != AstVisitAction . Continue ) break ;
}
}
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents an array expression, e.g. <c>@(1)</c>. The array literal (e.g. <c>1,2,3</c>) is
/// represented by <see cref="ArrayLiteralAst"/>.
/// </summary>
public class ArrayExpressionAst : ExpressionAst
{
/// <summary>
/// Construct an expression that forces the result to be an array.
/// </summary>
/// <param name="extent">The extent of the expression, including the opening '@(' and closing ')'.</param>
/// <param name="statementBlock">The statements executed as part of the expression.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="statementBlock"/> is null.
/// </exception>
public ArrayExpressionAst ( IScriptExtent extent , StatementBlockAst statementBlock )
: base ( extent )
{
if ( statementBlock == null )
{
throw PSTraceSource . NewArgumentNullException ( "statementBlock" );
}
this . SubExpression = statementBlock ;
SetParent ( statementBlock );
}
/// <summary>
/// The expression/statements represented by this sub-expression.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]
public StatementBlockAst SubExpression { get ; private set ; }
/// <summary>
/// Copy the ArrayExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newStatementBlock = CopyElement ( this . SubExpression );
return new ArrayExpressionAst ( this . Extent , newStatementBlock );
}
/// <summary>
/// The result of an ArrayExpressionAst is always <c>typeof(object[])</c>.
/// </summary>
public override Type StaticType { get { return typeof ( object []); } }
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
yield return new PSTypeName ( typeof ( object []));
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitArrayExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitArrayExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = SubExpression . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents an expression (or pipeline) that is enclosed in parentheses, e.g. <c>(1)</c> or <c>(dir)</c>
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Paren")]
public class ParenExpressionAst : ExpressionAst , ISupportsAssignment
{
/// <summary>
/// Construct a parenthesized expression.
/// </summary>
/// <param name="extent">The extent of the expression, including the opening and closing parentheses.</param>
/// <param name="pipeline">The pipeline (or expression) enclosed in parentheses.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="pipeline"/> is null.
/// </exception>
public ParenExpressionAst ( IScriptExtent extent , PipelineBaseAst pipeline )
: base ( extent )
{
if ( pipeline == null )
{
throw PSTraceSource . NewArgumentNullException ( "pipeline" );
}
this . Pipeline = pipeline ;
SetParent ( pipeline );
}
/// <summary>
/// The pipeline (which is frequently but not always an expression) for this parenthesized expression.
/// This property is never null.
/// </summary>
public PipelineBaseAst Pipeline { get ; private set ; }
/// <summary>
/// Copy the ParenExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newPipeline = CopyElement ( this . Pipeline );
return new ParenExpressionAst ( this . Extent , newPipeline );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return Pipeline . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitParenExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitParenExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Pipeline . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
IAssignableValue ISupportsAssignment . GetAssignableValue ()
{
return (( ISupportsAssignment ) Pipeline . GetPureExpression ()). GetAssignableValue ();
}
}
/// <summary>
/// The ast that represents a subexpression, e.g. <c>$(1)</c>.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]
public class SubExpressionAst : ExpressionAst
{
/// <summary>
/// Construct a subexpression.
/// </summary>
/// <param name="extent">The extent of the expression.</param>
/// <param name="statementBlock"></param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="statementBlock"/> is null.
/// </exception>
public SubExpressionAst ( IScriptExtent extent , StatementBlockAst statementBlock )
: base ( extent )
{
if ( statementBlock == null )
{
throw PSTraceSource . NewArgumentNullException ( "statementBlock" );
}
this . SubExpression = statementBlock ;
SetParent ( statementBlock );
}
/// <summary>
/// The expression/statements represented by this sub-expression. This property is never null.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]
public StatementBlockAst SubExpression { get ; private set ; }
/// <summary>
/// Copy the SubExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newStatementBlock = CopyElement ( this . SubExpression );
return new SubExpressionAst ( this . Extent , newStatementBlock );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return SubExpression . GetInferredType ( context );
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitSubExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitSubExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = SubExpression . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents a "using" expression, e.g. <c>$using:pshome</c>
/// </summary>
public class UsingExpressionAst : ExpressionAst
{
/// <summary>
/// Construct a using expression.
/// </summary>
/// <param name="extent">The extent of the using expression.</param>
/// <param name="expressionAst">The sub-expression of the using expression.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/> or <paramref name="expressionAst"/> is null.
/// </exception>
public UsingExpressionAst ( IScriptExtent extent , ExpressionAst expressionAst )
: base ( extent )
{
if ( expressionAst == null )
{
throw PSTraceSource . NewArgumentNullException ( "expressionAst" );
}
RuntimeUsingIndex = - 1 ;
this . SubExpression = expressionAst ;
SetParent ( SubExpression );
}
/// <summary>
/// The expression represented by this using expression. This property is never null.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]
public ExpressionAst SubExpression { get ; private set ; }
// Used from code gen to get the value from a well known location.
internal int RuntimeUsingIndex
{
get ;
set ;
}
/// <summary>
/// Copy the UsingExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newExpression = CopyElement ( this . SubExpression );
var newUsingExpression = new UsingExpressionAst ( this . Extent , newExpression );
newUsingExpression . RuntimeUsingIndex = this . RuntimeUsingIndex ;
return newUsingExpression ;
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
return SubExpression . GetInferredType ( context );
}
#region UsingExpression Utilities
internal const string UsingPrefix = "__using_" ;
/// <summary>
/// Get the underlying "using variable" from a UsingExpressionAst
/// </summary>
/// <param name="usingExpressionAst">
/// A UsingExpressionAst
/// </param>
/// <returns>
/// The underlying VariableExpressionAst of the UsingExpression
/// </returns>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We want to get the underlying variable only for the UsingExpressionAst.")]
public static VariableExpressionAst ExtractUsingVariable ( UsingExpressionAst usingExpressionAst )
{
if ( usingExpressionAst == null )
{
throw new ArgumentNullException ( "usingExpressionAst" );
}
return ExtractUsingVariableImpl ( usingExpressionAst );
}
/// <summary>
/// A UsingExpressionAst must contains a VariableExpressionAst
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
private static VariableExpressionAst ExtractUsingVariableImpl ( ExpressionAst expression )
{
var usingExpr = expression as UsingExpressionAst ;
VariableExpressionAst variableExpr ;
if ( usingExpr != null )
{
variableExpr = usingExpr . SubExpression as VariableExpressionAst ;
if ( variableExpr != null )
{
return variableExpr ;
}
return ExtractUsingVariableImpl ( usingExpr . SubExpression );
}
var indexExpr = expression as IndexExpressionAst ;
if ( indexExpr != null )
{
variableExpr = indexExpr . Target as VariableExpressionAst ;
if ( variableExpr != null )
{
return variableExpr ;
}
return ExtractUsingVariableImpl ( indexExpr . Target );
}
var memberExpr = expression as MemberExpressionAst ;
if ( memberExpr != null )
{
variableExpr = memberExpr . Expression as VariableExpressionAst ;
if ( variableExpr != null )
{
return variableExpr ;
}
return ExtractUsingVariableImpl ( memberExpr . Expression );
}
Diagnostics . Assert ( false , "We should alwasy be able to get a VariableExpressionAst from a UsingExpressionAst" );
return null ;
}
#endregion UsingExpression Utilities
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitUsingExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitUsingExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = SubExpression . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
}
/// <summary>
/// The ast that represents an index expression, e.g. <c>$a[0]</c>.
/// </summary>
public class IndexExpressionAst : ExpressionAst , ISupportsAssignment
{
/// <summary>
/// Construct an ast for an index expression.
/// </summary>
/// <param name="extent">The extent of the expression.</param>
/// <param name="target">The expression being indexed.</param>
/// <param name="index">The index expression.</param>
/// <exception cref="PSArgumentNullException">
/// If <paramref name="extent"/>, <paramref name="target"/>, or <paramref name="index"/> is null.
/// </exception>
public IndexExpressionAst ( IScriptExtent extent , ExpressionAst target , ExpressionAst index )
: base ( extent )
{
if ( target == null || index == null )
{
throw PSTraceSource . NewArgumentNullException ( target == null ? "target" : "index" );
}
this . Target = target ;
SetParent ( target );
this . Index = index ;
SetParent ( index );
}
/// <summary>
/// Return the ast for the expression being indexed. This value is never null.
/// </summary>
public ExpressionAst Target { get ; private set ; }
/// <summary>
/// Return the ast for the index expression. This value is never null.
/// </summary>
public ExpressionAst Index { get ; private set ; }
/// <summary>
/// Copy the IndexExpressionAst instance
/// </summary>
public override Ast Copy ()
{
var newTarget = CopyElement ( this . Target );
var newIndex = CopyElement ( this . Index );
return new IndexExpressionAst ( this . Extent , newTarget , newIndex );
}
internal override IEnumerable < PSTypeName > GetInferredType ( CompletionContext context )
{
var targetTypes = Target . GetInferredType ( context );
foreach ( var psType in targetTypes )
{
var type = psType . Type ;
if ( type != null )
{
if ( type . IsArray )
{
yield return new PSTypeName ( type . GetElementType ());
continue ;
}
foreach ( var i in type . GetInterfaces ())
{
if ( i . GetTypeInfo (). IsGenericType && i . GetGenericTypeDefinition () == typeof ( IDictionary <,>))
{
var valueType = i . GetGenericArguments ()[ 1 ];
if (! valueType . GetTypeInfo (). ContainsGenericParameters )
{
yield return new PSTypeName ( valueType );
}
}
}
var defaultMember = type . GetCustomAttributes < DefaultMemberAttribute >( true ). FirstOrDefault ();
if ( defaultMember != null )
{
var indexers =
type . GetMethods ( BindingFlags . Public | BindingFlags . Instance ). Where (
m => m . Name . Equals ( "get_" + defaultMember . MemberName ));
foreach ( var indexer in indexers )
{
yield return new PSTypeName ( indexer . ReturnType );
}
}
}
// Inferred type of target wasn't indexable. Assume (perhaps incorrectly)
// that it came from OutputType and that more than one object was returned
// and that we're indexing because of that, in which case, OutputType really
// is the inferred type.
yield return psType ;
}
}
#region Visitors
internal override object Accept ( ICustomAstVisitor visitor )
{
return visitor . VisitIndexExpression ( this );
}
internal override AstVisitAction InternalVisit ( AstVisitor visitor )
{
var action = visitor . VisitIndexExpression ( this );
if ( action == AstVisitAction . SkipChildren )
return visitor . CheckForPostAction ( this , AstVisitAction . Continue );
if ( action == AstVisitAction . Continue )
action = Target . InternalVisit ( visitor );
if ( action == AstVisitAction . Continue )
action = Index . InternalVisit ( visitor );
return visitor . CheckForPostAction ( this , action );
}
#endregion Visitors
IAssignableValue ISupportsAssignment . GetAssignableValue ()
{
return new IndexAssignableValue { IndexExpressionAst = this };
}
}
#endregion Expressions
#region Help
/// <summary>
/// The help content specified via help comments for a given script or script function.
/// </summary>
public sealed class CommentHelpInfo
{
/// <summary>
/// The help content of the .SYNOPSIS section, if specified, otherwise null.
/// </summary>
public string Synopsis { get ; internal set ; }
/// <summary>
/// The help content of the .DESCRIPTION section, if specified, otherwise null.
/// </summary>
public string Description { get ; internal set ; }
/// <summary>
/// The help content of the .NOTES section, if specified, otherwise null.
/// </summary>
public string Notes { get ; internal set ; }
/// <summary>
/// The help content for each parameter where help content is specified. The
/// key is the parameter name, the value is the help content.
/// </summary>
/// TODO, Changing this to an IDictionary because ReadOnlyDictionary is available only in .NET 4.5
/// This is a temporary workaround and will be fixed later. Tracked by Win8: 354135
public IDictionary < string , string > Parameters { get ; internal set ; }
/// <summary>
/// The help content from all of the specified .LINK sections.
/// </summary>
public ReadOnlyCollection < string > Links { get ; internal set ; }
/// <summary>
/// The help content from all of the specified .EXAMPLE sections.
/// </summary>
public ReadOnlyCollection < string > Examples { get ; internal set ; }
/// <summary>
/// The help content from all of the specified .INPUT sections.
/// </summary>
public ReadOnlyCollection < string > Inputs { get ; internal set ; }
/// <summary>
/// The help content from all of the specified .OUTPUT sections.
/// </summary>
public ReadOnlyCollection < string > Outputs { get ; internal set ; }
/// <summary>
/// The help content of the .COMPONENT section, if specified, otherwise null.
/// </summary>
public string Component { get ; internal set ; }
/// <summary>
/// The help content of the .ROLE section, if specified, otherwise null.
/// </summary>
public string Role { get ; internal set ; }
/// <summary>
/// The help content of the .FUNCTIONALITY section, if specified, otherwise null.
/// </summary>
public string Functionality { get ; internal set ; }
/// <summary>
/// The help content of the .FORWARDHELPTARGETNAME section, if specified, otherwise null.
/// </summary>
public string ForwardHelpTargetName { get ; internal set ; }
/// <summary>
/// The help content of the .FORWARDHELPCATEGORY section, if specified, otherwise null.
/// </summary>
public string ForwardHelpCategory { get ; internal set ; }
/// <summary>
/// The help content of the .REMOTEHELPRUNSPACE section, if specified, otherwise null.
/// </summary>
public string RemoteHelpRunspace { get ; internal set ; }
/// <summary>
/// The help content of the .MAMLHELPFILE section, if specified, otherwise null.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Maml")]
public string MamlHelpFile { get ; internal set ; }
/// <summary>
/// Returns the help info as a comment block.
/// </summary>
public string GetCommentBlock ()
{
var sb = new StringBuilder ();
sb . AppendLine ( "<#" );
if (! string . IsNullOrEmpty ( Synopsis ))
{
sb . AppendLine ( ".SYNOPSIS" );
sb . AppendLine ( Synopsis );
}
if (! string . IsNullOrEmpty ( Description ))
{
sb . AppendLine ( ".DESCRIPTION" );
sb . AppendLine ( Description );
}
foreach ( var parameter in Parameters )
{
sb . Append ( ".PARAMETER " );
sb . AppendLine ( parameter . Key );
sb . AppendLine ( parameter . Value );
}
for ( int index = 0 ; index < Inputs . Count ; index ++)
{
var input = Inputs [ index ];
sb . AppendLine ( ".INPUTS" );
sb . AppendLine ( input );
}
for ( int index = 0 ; index < Outputs . Count ; index ++)
{
var output = Outputs [ index ];
sb . AppendLine ( ".OUTPUTS" );
sb . AppendLine ( output );
}
if (! string . IsNullOrEmpty ( Notes ))
{
sb . AppendLine ( ".NOTES" );
sb . AppendLine ( Notes );
}
for ( int index = 0 ; index < Examples . Count ; index ++)
{
var example = Examples [ index ];
sb . AppendLine ( ".EXAMPLE" );
sb . AppendLine ( example );
}
for ( int index = 0 ; index < Links . Count ; index ++)
{
var link = Links [ index ];
sb . AppendLine ( ".LINK" );
sb . AppendLine ( link );
}
if (! string . IsNullOrEmpty ( ForwardHelpTargetName ))
{
sb . Append ( ".FORWARDHELPTARGETNAME " );
sb . AppendLine ( ForwardHelpTargetName );
}
if (! string . IsNullOrEmpty ( ForwardHelpCategory ))
{
sb . Append ( ".FORWARDHELPCATEGORY " );
sb . AppendLine ( ForwardHelpCategory );
}
if (! string . IsNullOrEmpty ( RemoteHelpRunspace ))
{
sb . Append ( ".REMOTEHELPRUNSPACE " );
sb . AppendLine ( RemoteHelpRunspace );
}
if (! string . IsNullOrEmpty ( Component ))
{
sb . AppendLine ( ".COMPONENT" );
sb . AppendLine ( Component );
}
if (! string . IsNullOrEmpty ( Role ))
{
sb . AppendLine ( ".ROLE" );
sb . AppendLine ( Role );
}
if (! string . IsNullOrEmpty ( Functionality ))
{
sb . AppendLine ( ".FUNCTIONALITY" );
sb . AppendLine ( Functionality );
}
if (! string . IsNullOrEmpty ( MamlHelpFile ))
{
sb . Append ( ".EXTERNALHELP " );
sb . AppendLine ( MamlHelpFile );
}
sb . AppendLine ( "#>" );
return sb . ToString ();
}
}
#endregion Help
}
namespace System.Management.Automation.Internal
{
using Runspaces ;
using Language ;
/// <summary>Internal interface used for workflow compilation.</summary>
[SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")]
public interface IAstToWorkflowConverter
{
/// <summary/>
List < ParseError > ValidateAst ( FunctionDefinitionAst ast );
/// <summary/>
List < WorkflowInfo > CompileWorkflows ( ScriptBlockAst ast , PSModuleInfo definingModule );
/// <summary/>
List < WorkflowInfo > CompileWorkflows ( ScriptBlockAst ast , PSModuleInfo definingModule , InitialSessionState initialSessionState , out ParseException parsingErrors );
/// <summary/>
List < WorkflowInfo > CompileWorkflows ( ScriptBlockAst ast , PSModuleInfo definingModule , string rootWorkflowName );
/// <summary/>
List < WorkflowInfo > CompileWorkflows ( ScriptBlockAst ast , PSModuleInfo definingModule , InitialSessionState initialSessionState , out ParseException parsingErrors , string rootWorkflowName );
/// <summary/>
WorkflowInfo CompileWorkflow ( string name , string definition , InitialSessionState initialSessionState );
}
}