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 @@ -6862,6 +6862,11 @@ public object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst)
return arrayLiteralAst.Elements.All(e => (bool)e.Accept(this));
}

public object VisitListLiteral(ListLiteralAst listExpressionAst)
{
return listExpressionAst.Elements.All(e => (bool)e.Accept(this));
}

public object VisitHashtable(HashtableAst hashtableAst)
{
foreach (var keyValuePair in hashtableAst.KeyValuePairs)
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should add this to the interpreter - this way it stays as close as possible to original and/or the one used for dynamic when now JIT is available.

It is possible to add new instructions without touching the interpreter it that's how you want to do it - see PowerShellLoopExpression for an example.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NewListInitInstruction is a minor optimization which can be removed.
But I need to implement LightCompiler.CompileListInitExpression because it's currently not implemented. Are you suggesting to not use ListInitExpression in our compiler generated code?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the method is now implemented: http://source.dot.net/#System.Linq.Expressions/System/Linq/Expressions/Interpreter/LightCompiler.cs,a988c37e1da7a799

I wonder if there is a supported way of invoking the interpreter now? We'd have to make sure they have any of our fixes first, but it'd be nice to not ship two copies of basically the same thing.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, I can use their implementation then.

{
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think numbered interfaces made sense when the release cycle spanned years, but I'm worried we'll end up with ICustomAstVistor9 before too long.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One (maybe not awesome) idea is to avoid the problem for this feature is to add a bool flag to ArrayLiteralAst - because they should be structurally the same.

The downside to this approach is that some analysis tools that aren't aware of list literals will analyze the ast as an array, e.g. they may incorrectly give warnings about performance.

@daxian-dbw Dongbo Wang (daxian-dbw) Dec 29, 2017 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about reusing the ArrayLiteralAst, but didn't go that direction for two reasons:

  1. An explicit ListLiteralAst is more discoverable.
  2. ArrayLiteralAst can appear on the left side of an assignment. I didn't plan to make [] also assignable. (but I don't object to the idea to make it capable of the same).

I would like to see more discussion on this before updating the code.

{
/// <summary/>
object VisitListLiteral(ListLiteralAst listLiteralAst);
}

#if DEBUG
class CheckAllParentsSet : AstVisitor2
class CheckAllParentsSet : AstVisitor3
{
internal CheckAllParentsSet(Ast root)
{
Expand Down Expand Up @@ -239,6 +246,7 @@ internal AstVisitAction CheckParent(Ast ast)
public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressionAst 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 VisitListLiteral(ListLiteralAst ast) { return CheckParent(ast); }
public override AstVisitAction VisitHashtable(HashtableAst ast) { return CheckParent(ast); }
public override AstVisitAction VisitScriptBlockExpression(ScriptBlockExpressionAst ast) { return CheckParent(ast); }
public override AstVisitAction VisitParenExpression(ParenExpressionAst 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 @@ -408,6 +416,7 @@ protected AstVisitAction CheckScriptBlock(Ast ast)
public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressionAst 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 VisitListLiteral(ListLiteralAst ast) { return Check(ast); }
public override AstVisitAction VisitHashtable(HashtableAst ast) { return Check(ast); }
public override AstVisitAction VisitScriptBlockExpression(ScriptBlockExpressionAst ast) { return CheckScriptBlock(ast); }
public override AstVisitAction VisitParenExpression(ParenExpressionAst 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 VisitListLiteral(ListLiteralAst listExpressionAst) { return null; }
}
}
33 changes: 28 additions & 5 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ internal static class CachedReflectionInfo
typeof(List<object>).GetConstructor(PSTypeExtensions.EmptyTypes);
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 @@ -547,8 +549,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_ctor);

internal static readonly Expression BoxedTrue = Expression.Field(null,
typeof(Boxed).GetField("True", BindingFlags.Static | BindingFlags.NonPublic));
Expand Down Expand Up @@ -678,7 +680,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 +1882,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 +3187,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 @@ -5537,6 +5539,27 @@ public object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst)
arrayLiteralAst.Elements.Select(elem => Compile(elem).Cast(typeof(object))));
}

public object VisitListLiteral(ListLiteralAst listLiteralAst)
{
if (listLiteralAst.Elements.Count == 0)
{
return ExpressionCache.NewEmptyObjectList;
}

List<Expression> elementValues = new List<Expression>(listLiteralAst.Elements.Count);
foreach (var element in listLiteralAst.Elements)
{
var eValue = Compile(element);
if (eValue.Type != typeof(void)) { elementValues.Add(eValue.Cast(typeof(object))); }
}

if (elementValues.Count == 0)
{
return ExpressionCache.NewEmptyObjectList;
}
return Expression.ListInit(ExpressionCache.NewEmptyObjectList, CachedReflectionInfo.ObjectList_Add, elementValues);
}

private IEnumerable<Expression> BuildHashtable(ReadOnlyCollection<KeyValuePair> keyValuePairs, ParameterExpression temp, bool ordered)
{
yield return Expression.Assign(temp,
Expand Down
Loading