Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6702,7 +6702,7 @@ public int Compare(PSObject x, PSObject y)
/// use is for intellisense where you don't want to run arbitrary code, but you do want to know the values
/// of various expressions so you can get the members.
/// </summary>
internal class SafeExprEvaluator : ICustomAstVisitor2
internal class SafeExprEvaluator : ICustomAstVisitor3
{
internal static bool TrySafeEval(ExpressionAst ast, ExecutionContext executionContext, out object value)
{
Expand Down Expand Up @@ -6852,6 +6852,11 @@ public object VisitIndexExpression(IndexExpressionAst indexExpressionAst)
return (bool)indexExpressionAst.Target.Accept(this) && (bool)indexExpressionAst.Index.Accept(this);
}

public object VisitListExpression(ListExpressionAst listExpressionAst)
{
return listExpressionAst.SubExpression.Accept(this);
}

public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst)
{
return arrayExpressionAst.SubExpression.Accept(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
*
* ***************************************************************************/

using System.Collections.Generic;

namespace System.Management.Automation.Interpreter
{
internal sealed class NewArrayInitInstruction<TElement> : Instruction
Expand All @@ -39,6 +41,40 @@ public override int Run(InterpretedFrame frame)
}
}

internal sealed class NewListInitInstruction<TElement> : Instruction
{
private readonly int _elementCount;

internal NewListInitInstruction(int elementCount)
{
_elementCount = elementCount;
}

public override int ConsumedStack { get { return _elementCount; } }
public override int ProducedStack { get { return 1; } }

public override int Run(InterpretedFrame frame)
{
List<TElement> list;
if (_elementCount == 1)
{
list = new List<TElement>() { (TElement)frame.Pop() };
}
else
{
TElement[] array = new TElement[_elementCount];
for (int i = _elementCount - 1; i >= 0; i--)
{
array[i] = (TElement)frame.Pop();
}
list = new List<TElement>(array);
}

frame.Push(list);
return +1;
}
}

internal sealed class NewArrayInstruction<TElement> : Instruction
{
internal NewArrayInstruction() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ internal static InstructionFactory GetFactory(Type type)
protected internal abstract Instruction DefaultValue();
protected internal abstract Instruction NewArray();
protected internal abstract Instruction NewArrayInit(int elementCount);
protected internal abstract Instruction NewListInit(int elementCount);
}

internal sealed class InstructionFactory<T> : InstructionFactory
Expand All @@ -75,6 +76,7 @@ internal sealed class InstructionFactory<T> : InstructionFactory
private Instruction _newArray;
private Instruction _typeAs;
private Instruction[] _newArrayInit;
private Instruction[] _newListInit;
// This number is somewhat arbitrary - trying to avoid some gc without keeping
// objects (instructions) around that aren't used that often.
private const int MaxArrayInitElementCountCache = 32;
Expand Down Expand Up @@ -124,5 +126,19 @@ protected internal override Instruction NewArrayInit(int elementCount)
}
return new NewArrayInitInstruction<T>(elementCount);
}

protected internal override Instruction NewListInit(int elementCount)
{
if (elementCount < MaxArrayInitElementCountCache)
{
if (_newListInit == null)
{
_newListInit = new Instruction[MaxArrayInitElementCountCache];
}

return _newListInit[elementCount] ?? (_newListInit[elementCount] = new NewListInitInstruction<T>(elementCount));
}
return new NewListInitInstruction<T>(elementCount);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,20 @@ public void EmitNewArrayInit(Type elementType, int elementCount)
}
}

public void EmitNewListInit(Type elementType, int elementCount)
{
// To avoid lock contention in InstructionFactory.GetFactory, we special case the most common
// types of lists that the compiler creates.
if (elementType == typeof(object))
{
Emit(InstructionFactory<object>.Factory.NewListInit(elementCount));
}
else
{
Emit(InstructionFactory.GetFactory(elementType).NewListInit(elementCount));
}
}

#endregion

#region Arithmetic Operations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1803,7 +1803,40 @@ private void CompileInvocationExpression(Expression expr)
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "expr")]
private void CompileListInitExpression(Expression expr)
{
throw new System.NotImplementedException();
var node = (ListInitExpression) expr;
#if !DEBUG
// Optimize the most common use -- create and initialize an object list
// - Use the default constructor of List<object>
// - Initializer collection is not empty and the AddMethod is 'Add(object)'
if (node.NewExpression == System.Management.Automation.Language.ExpressionCache.NewEmptyObjectList &&
node.Initializers.Count > 0 &&
node.Initializers.TrueForAll(e => e.AddMethod == System.Management.Automation.Language.CachedReflectionInfo.ObjectList_Add))
{
for (int index = 0; index < node.Initializers.Count; index++)
{
var arg = node.Initializers[index].Arguments[0];
Compile(arg);
}

_instructions.EmitNewListInit(typeof(object), node.Initializers.Count);
return;
}
#endif
CompileNewExpression(node.NewExpression);
if (node.Initializers.Count > 0)
{
LocalDefinition local = _locals.DefineLocal(Expression.Parameter(node.Type), _instructions.Count);
_instructions.EmitAssignLocal(local.Index);

foreach (ElementInit eInit in node.Initializers)
{
foreach (Expression arg in eInit.Arguments) { Compile(arg); }
_instructions.EmitCall(eInit.AddMethod);
_instructions.EmitLoadLocal(local.Index);
}

_locals.UndefineLocal(local, _instructions.Count);
}
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "expr")]
Expand Down
24 changes: 21 additions & 3 deletions src/System.Management.Automation/engine/parser/AstVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,15 @@ public interface ICustomAstVisitor2 : ICustomAstVisitor
object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst);
}

/// <summary/>
public interface ICustomAstVisitor3 : ICustomAstVisitor2
{
/// <summary/>
object VisitListExpression(ListExpressionAst listExpressionAst);
}

#if DEBUG
class CheckAllParentsSet : AstVisitor2
class CheckAllParentsSet : AstVisitor3
{
internal CheckAllParentsSet(Ast root)
{
Expand Down Expand Up @@ -237,6 +244,7 @@ internal AstVisitAction CheckParent(Ast ast)
public override AstVisitAction VisitVariableExpression(VariableExpressionAst ast) { return CheckParent(ast); }
public override AstVisitAction VisitMemberExpression(MemberExpressionAst ast) { return CheckParent(ast); }
public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressionAst ast) { return CheckParent(ast); }
public override AstVisitAction VisitListExpression(ListExpressionAst ast) { return CheckParent(ast); }
public override AstVisitAction VisitArrayExpression(ArrayExpressionAst ast) { return CheckParent(ast); }
public override AstVisitAction VisitArrayLiteral(ArrayLiteralAst ast) { return CheckParent(ast); }
public override AstVisitAction VisitHashtable(HashtableAst ast) { return CheckParent(ast); }
Expand All @@ -258,7 +266,7 @@ internal AstVisitAction CheckParent(Ast ast)
/// <summary>
/// Check if <see cref="TypeConstraintAst"/> contains <see cref="TypeBuilder "/> type
/// </summary>
class CheckTypeBuilder : AstVisitor2
class CheckTypeBuilder : AstVisitor3
{
public override AstVisitAction VisitTypeConstraint(TypeConstraintAst ast)
{
Expand All @@ -275,7 +283,7 @@ public override AstVisitAction VisitTypeConstraint(TypeConstraintAst ast)
/// <summary>
/// Searches an AST, using the evaluation function provided by either of the constructors
/// </summary>
internal class AstSearcher : AstVisitor2
internal class AstSearcher : AstVisitor3
{
#region External interface

Expand Down Expand Up @@ -406,6 +414,7 @@ protected AstVisitAction CheckScriptBlock(Ast ast)
public override AstVisitAction VisitVariableExpression(VariableExpressionAst ast) { return Check(ast); }
public override AstVisitAction VisitMemberExpression(MemberExpressionAst ast) { return Check(ast); }
public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressionAst ast) { return Check(ast); }
public override AstVisitAction VisitListExpression(ListExpressionAst ast) { return Check(ast); }
public override AstVisitAction VisitArrayExpression(ArrayExpressionAst ast) { return Check(ast); }
public override AstVisitAction VisitArrayLiteral(ArrayLiteralAst ast) { return Check(ast); }
public override AstVisitAction VisitHashtable(HashtableAst ast) { return Check(ast); }
Expand Down Expand Up @@ -559,4 +568,13 @@ public abstract class DefaultCustomAstVisitor2 : DefaultCustomAstVisitor, ICusto
/// <summary/>
public virtual object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return null; }
}

/// <summary>
/// Default implementation of <see cref="ICustomAstVisitor3"/> interface
/// </summary>
public abstract class DefaultCustomAstVisitor3 : DefaultCustomAstVisitor2, ICustomAstVisitor3
{
/// <summary/>
public virtual object VisitListExpression(ListExpressionAst listExpressionAst) { return null; }
}
}
63 changes: 57 additions & 6 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ internal static class CachedReflectionInfo
internal const BindingFlags staticPublicFlags = BindingFlags.Static | BindingFlags.Public;
internal const BindingFlags instancePublicFlags = BindingFlags.Instance | BindingFlags.Public;

internal static readonly ConstructorInfo ObjectList_ctor =
internal static readonly ConstructorInfo ObjectList_Default_ctor =
typeof(List<object>).GetConstructor(PSTypeExtensions.EmptyTypes);
internal static readonly ConstructorInfo ObjectList_IEnumerable_ctor =
typeof(List<object>).GetConstructor(new Type[] { typeof(IEnumerable<object>) });
internal static readonly MethodInfo ObjectList_ToArray =
typeof(List<object>).GetMethod(nameof(List<object>.ToArray), PSTypeExtensions.EmptyTypes);
internal static readonly MethodInfo ObjectList_Add =
typeof(List<object>).GetMethod(nameof(List<object>.Add));

internal static readonly MethodInfo ArrayOps_GetMDArrayValue =
typeof(ArrayOps).GetMethod(nameof(ArrayOps.GetMDArrayValue), staticFlags);
Expand Down Expand Up @@ -124,6 +128,8 @@ internal static class CachedReflectionInfo
typeof(EnumerableOps).GetMethod(nameof(EnumerableOps.SlicingIndex), staticFlags);
internal static readonly MethodInfo EnumerableOps_ToArray =
typeof(EnumerableOps).GetMethod(nameof(EnumerableOps.ToArray), staticFlags);
internal static readonly MethodInfo EnumerableOps_ToList =
typeof(EnumerableOps).GetMethod(nameof(EnumerableOps.ToList), staticFlags);
internal static readonly MethodInfo EnumerableOps_WriteEnumerableToPipe =
typeof(EnumerableOps).GetMethod(nameof(EnumerableOps.WriteEnumerableToPipe), staticFlags);

Expand Down Expand Up @@ -547,8 +553,8 @@ internal static class ExpressionCache
internal static readonly Expression CurrentCultureIgnoreCaseComparer = Expression.Constant(StringComparer.CurrentCultureIgnoreCase, typeof(StringComparer));
internal static readonly Expression CatchAllType = Expression.Constant(typeof(ExceptionHandlingOps.CatchAll), typeof(Type));
internal static readonly Expression Empty = Expression.Empty();
internal static Expression GetExecutionContextFromTLS =
Expression.Call(CachedReflectionInfo.LocalPipeline_GetExecutionContextFromTLS);
internal static readonly Expression GetExecutionContextFromTLS = Expression.Call(CachedReflectionInfo.LocalPipeline_GetExecutionContextFromTLS);
internal static readonly NewExpression NewEmptyObjectList = Expression.New(CachedReflectionInfo.ObjectList_Default_ctor);

internal static readonly Expression BoxedTrue = Expression.Field(null,
typeof(Boxed).GetField("True", BindingFlags.Static | BindingFlags.NonPublic));
Expand Down Expand Up @@ -678,7 +684,7 @@ internal void PopTrapHandlers()
}
}

internal class Compiler : ICustomAstVisitor2
internal class Compiler : ICustomAstVisitor3
{
internal static readonly ParameterExpression _executionContextParameter;
internal static readonly ParameterExpression _functionContext;
Expand Down Expand Up @@ -1880,7 +1886,7 @@ private Expression CaptureAstResults(
temps.Add(resultList);
temps.Add(oldPipe);
exprs.Add(Expression.Assign(oldPipe, s_getCurrentPipe));
exprs.Add(Expression.Assign(resultList, Expression.New(CachedReflectionInfo.ObjectList_ctor)));
exprs.Add(Expression.Assign(resultList, ExpressionCache.NewEmptyObjectList));
exprs.Add(Expression.Assign(s_getCurrentPipe, Expression.New(CachedReflectionInfo.Pipe_ctor, resultList)));
exprs.Add(Expression.Call(oldPipe, CachedReflectionInfo.Pipe_SetVariableListForTemporaryPipe, s_getCurrentPipe));
if (generateRedirectExprs != null)
Expand Down Expand Up @@ -3185,7 +3191,7 @@ private Expression GetRedirectedExpression(CommandExpressionAst commandExpr, boo
temps.Add(resultList);
temps.Add(oldPipe);
exprs.Add(Expression.Assign(oldPipe, s_getCurrentPipe));
exprs.Add(Expression.Assign(resultList, Expression.New(CachedReflectionInfo.ObjectList_ctor)));
exprs.Add(Expression.Assign(resultList, ExpressionCache.NewEmptyObjectList));
exprs.Add(Expression.Assign(s_getCurrentPipe, Expression.New(CachedReflectionInfo.Pipe_ctor, resultList)));
exprs.Add(Expression.Call(oldPipe, CachedReflectionInfo.Pipe_SetVariableListForTemporaryPipe, s_getCurrentPipe));
}
Expand Down Expand Up @@ -5483,6 +5489,51 @@ public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMember
args, invokeMemberExpressionAst.Static, false);
}

public object VisitListExpression(ListExpressionAst listExpressionAst)
{
ExpressionAst pureExprAst = null;

var subExpr = listExpressionAst.SubExpression;
if (subExpr.Traps == null)
{
if (subExpr.Statements.Count == 1)
{
var pipelineBase = subExpr.Statements[0] as PipelineBaseAst;
if (pipelineBase != null)
{
pureExprAst = pipelineBase.GetPureExpression();
}
}
else if (subExpr.Statements.Count == 0)
{
return ExpressionCache.NewEmptyObjectList;
}
}

if (pureExprAst is ArrayLiteralAst arrayLiteral)
{
// Avoid generating an object array
return Expression.ListInit(ExpressionCache.NewEmptyObjectList, CachedReflectionInfo.ObjectList_Add,
arrayLiteral.Elements.Select(e => Compile(e).Cast(typeof(object))));
}

Expression values = pureExprAst != null ? Compile(pureExprAst) : CaptureAstResults(subExpr, CaptureAstContext.Enumerable);

if (values.Type == typeof(List<object>)) { return values; }
if (values.Type.IsPrimitive || values.Type == typeof(string))
{
// Slight optimization - no need for a dynamic site. We could special case other
// types as well, but it's probably not worth it.
return Expression.ListInit(ExpressionCache.NewEmptyObjectList, CachedReflectionInfo.ObjectList_Add, values.Cast(typeof(object)));
}
if (values.Type == typeof(void))
{
// A dynamic site can't take void - but a void value is just an empty list.
return ExpressionCache.NewEmptyObjectList;
}
return DynamicExpression.Dynamic(PSToObjectListBinder.Get(), typeof(List<object>), values);
}

public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst)
{
Expression values = null;
Expand Down
Loading