From 58d065d5c3130320c83c1b3a6db0ac36b2702fa7 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Thu, 28 Dec 2017 12:03:52 -0800 Subject: [PATCH 1/3] ListExpression: engine changes --- .../CommandCompletion/CompletionCompleters.cs | 7 +- .../engine/interpreter/LightCompiler.cs | 17 ++++- .../engine/parser/AstVisitor.cs | 24 ++++++- .../engine/parser/Compiler.cs | 63 +++++++++++++++-- .../engine/parser/ConstantValues.cs | 46 ++++++------ .../engine/parser/PreOrderVisitor.cs | 13 +++- .../engine/parser/SafeValues.cs | 31 +++++++- .../engine/parser/TypeInferenceVisitor.cs | 7 +- .../engine/parser/VariableAnalysis.cs | 16 ++++- .../engine/parser/ast.cs | 70 +++++++++++++++++++ .../engine/runtime/Binding/Binders.cs | 59 ++++++++++++++++ .../engine/runtime/Operations/MiscOps.cs | 8 ++- .../resources/ParserStrings.resx | 3 + 13 files changed, 323 insertions(+), 41 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 39a9860df95..a02764a6cff 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -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. /// - internal class SafeExprEvaluator : ICustomAstVisitor2 + internal class SafeExprEvaluator : ICustomAstVisitor3 { internal static bool TrySafeEval(ExpressionAst ast, ExecutionContext executionContext, out object value) { @@ -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); diff --git a/src/System.Management.Automation/engine/interpreter/LightCompiler.cs b/src/System.Management.Automation/engine/interpreter/LightCompiler.cs index 09236a3ec5d..12450c12945 100644 --- a/src/System.Management.Automation/engine/interpreter/LightCompiler.cs +++ b/src/System.Management.Automation/engine/interpreter/LightCompiler.cs @@ -1803,7 +1803,22 @@ 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; + 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")] diff --git a/src/System.Management.Automation/engine/parser/AstVisitor.cs b/src/System.Management.Automation/engine/parser/AstVisitor.cs index de4255de363..8218632d8cc 100644 --- a/src/System.Management.Automation/engine/parser/AstVisitor.cs +++ b/src/System.Management.Automation/engine/parser/AstVisitor.cs @@ -175,8 +175,15 @@ public interface ICustomAstVisitor2 : ICustomAstVisitor object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst); } + /// + public interface ICustomAstVisitor3 : ICustomAstVisitor2 + { + /// + object VisitListExpression(ListExpressionAst listExpressionAst); + } + #if DEBUG - class CheckAllParentsSet : AstVisitor2 + class CheckAllParentsSet : AstVisitor3 { internal CheckAllParentsSet(Ast root) { @@ -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); } @@ -258,7 +266,7 @@ internal AstVisitAction CheckParent(Ast ast) /// /// Check if contains type /// - class CheckTypeBuilder : AstVisitor2 + class CheckTypeBuilder : AstVisitor3 { public override AstVisitAction VisitTypeConstraint(TypeConstraintAst ast) { @@ -275,7 +283,7 @@ public override AstVisitAction VisitTypeConstraint(TypeConstraintAst ast) /// /// Searches an AST, using the evaluation function provided by either of the constructors /// - internal class AstSearcher : AstVisitor2 + internal class AstSearcher : AstVisitor3 { #region External interface @@ -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); } @@ -559,4 +568,13 @@ public abstract class DefaultCustomAstVisitor2 : DefaultCustomAstVisitor, ICusto /// public virtual object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return null; } } + + /// + /// Default implementation of interface + /// + public abstract class DefaultCustomAstVisitor3 : DefaultCustomAstVisitor2, ICustomAstVisitor3 + { + /// + public virtual object VisitListExpression(ListExpressionAst listExpressionAst) { return null; } + } } diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 65b94436723..01e348deb65 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -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).GetConstructor(PSTypeExtensions.EmptyTypes); + internal static readonly ConstructorInfo ObjectList_IEnumerable_ctor = + typeof(List).GetConstructor(new Type[] { typeof(IEnumerable) }); internal static readonly MethodInfo ObjectList_ToArray = typeof(List).GetMethod(nameof(List.ToArray), PSTypeExtensions.EmptyTypes); + internal static readonly MethodInfo ObjectList_Add = + typeof(List).GetMethod(nameof(List.Add)); internal static readonly MethodInfo ArrayOps_GetMDArrayValue = typeof(ArrayOps).GetMethod(nameof(ArrayOps.GetMDArrayValue), staticFlags); @@ -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); @@ -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)); @@ -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; @@ -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) @@ -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)); } @@ -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)) { 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), values); + } + public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) { Expression values = null; diff --git a/src/System.Management.Automation/engine/parser/ConstantValues.cs b/src/System.Management.Automation/engine/parser/ConstantValues.cs index 8f6f0df41db..e943cd8e458 100644 --- a/src/System.Management.Automation/engine/parser/ConstantValues.cs +++ b/src/System.Management.Automation/engine/parser/ConstantValues.cs @@ -16,7 +16,7 @@ namespace System.Management.Automation.Language * There is a number of similarities between these two classes, and changes (fixes) in this code * may need to be reflected in that class and vice versa */ - internal class IsConstantValueVisitor : ICustomAstVisitor + internal class IsConstantValueVisitor : ICustomAstVisitor3 { public static bool IsConstant(Ast ast, out object constantValue, bool forAttribute = false, bool forRequires = false) { @@ -93,6 +93,18 @@ public static bool IsConstant(Ast ast, out object constantValue, bool forAttribu public object VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst) { return false; } public object VisitBlockStatement(BlockStatementAst blockStatementAst) { return false; } public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { return false; } + public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) { return false; } + public object VisitListExpression(ListExpressionAst listExpressionAst) { return false; } + public object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { return false; } + public object VisitPropertyMember(PropertyMemberAst propertyMemberAst) { return false; } + public object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return false; } + public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst) { return false; } + public object VisitUsingStatement(UsingStatementAst usingStatement) { return false; } + public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { return false; } + public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst) { return false; } + + public object VisitConstantExpression(ConstantExpressionAst constantExpressionAst) { return true; } + public object VisitStringConstantExpression(StringConstantExpressionAst stringConstantExpressionAst) { return true; } public object VisitStatementBlock(StatementBlockAst statementBlockAst) { @@ -165,16 +177,6 @@ public object VisitConvertExpression(ConvertExpressionAst convertExpressionAst) return (bool)convertExpressionAst.Child.Accept(this); } - public object VisitConstantExpression(ConstantExpressionAst constantExpressionAst) - { - return true; - } - - public object VisitStringConstantExpression(StringConstantExpressionAst stringConstantExpressionAst) - { - return true; - } - public object VisitSubExpression(SubExpressionAst subExpressionAst) { return subExpressionAst.SubExpression.Accept(this); @@ -229,11 +231,6 @@ public object VisitMemberExpression(MemberExpressionAst memberExpressionAst) return (((FieldInfo)memberInfo[0]).Attributes & FieldAttributes.Literal) != 0; } - public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) - { - return false; - } - public object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst) { // An array literal is a constant when we're generating metadata, but when @@ -262,7 +259,7 @@ public object VisitParenExpression(ParenExpressionAst parenExpressionAst) } } - internal class ConstantValueVisitor : ICustomAstVisitor + internal class ConstantValueVisitor : ICustomAstVisitor3 { internal bool AttributeArgument { get; set; } internal bool RequiresArgument { get; set; } @@ -325,6 +322,15 @@ private static object CompileAndInvoke(Ast ast) public object VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst) { return AutomationNull.Value; } public object VisitBlockStatement(BlockStatementAst blockStatementAst) { return AutomationNull.Value; } public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { return AutomationNull.Value; } + public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) { return AutomationNull.Value; } + public object VisitListExpression(ListExpressionAst listExpressionAst) { return AutomationNull.Value; } + public object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { return AutomationNull.Value; } + public object VisitPropertyMember(PropertyMemberAst propertyMemberAst) { return AutomationNull.Value; } + public object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return AutomationNull.Value; } + public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst) { return AutomationNull.Value; } + public object VisitUsingStatement(UsingStatementAst usingStatement) { return AutomationNull.Value; } + public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { return AutomationNull.Value; } + public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst) { return AutomationNull.Value; } public object VisitStatementBlock(StatementBlockAst statementBlockAst) { @@ -411,12 +417,6 @@ public object VisitMemberExpression(MemberExpressionAst memberExpressionAst) return ((FieldInfo)memberInfo[0]).GetValue(null); } - public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) - { - CheckIsConstant(arrayExpressionAst, "Caller to verify ast is constant"); - return arrayExpressionAst.SubExpression.Accept(this); - } - public object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst) { CheckIsConstant(arrayLiteralAst, "Caller to verify ast is constant"); diff --git a/src/System.Management.Automation/engine/parser/PreOrderVisitor.cs b/src/System.Management.Automation/engine/parser/PreOrderVisitor.cs index 62defcc9a58..537081c5cbb 100644 --- a/src/System.Management.Automation/engine/parser/PreOrderVisitor.cs +++ b/src/System.Management.Automation/engine/parser/PreOrderVisitor.cs @@ -162,7 +162,7 @@ internal AstVisitAction CheckForPostAction(Ast ast, AstVisitAction action) } /// - /// AstVisitor for new Ast node types + /// AstVisitor for new Ast node types added in PSv5.0 /// public abstract class AstVisitor2 : AstVisitor { @@ -189,7 +189,16 @@ public abstract class AstVisitor2 : AstVisitor } /// - /// Implement this interface when you implement or when + /// AstVisitor for new Ast node types added in PSv6.1 + /// + public abstract class AstVisitor3 : AstVisitor2 + { + /// + public virtual AstVisitAction VisitListExpression(ListExpressionAst listExpressionAst) { return AstVisitAction.Continue; } + } + + /// + /// Implement this interface when you implement , or when /// you want to do something after possibly visiting the children of the ast. /// public interface IAstPostVisitHandler diff --git a/src/System.Management.Automation/engine/parser/SafeValues.cs b/src/System.Management.Automation/engine/parser/SafeValues.cs index ed1924382de..3dbfac7d11b 100644 --- a/src/System.Management.Automation/engine/parser/SafeValues.cs +++ b/src/System.Management.Automation/engine/parser/SafeValues.cs @@ -3,6 +3,7 @@ --********************************************************************/ using System.Collections; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Management.Automation.Internal; @@ -37,7 +38,7 @@ namespace System.Management.Automation.Language { - internal class IsSafeValueVisitor : ICustomAstVisitor + internal class IsSafeValueVisitor : ICustomAstVisitor3 { public static bool IsAstSafe(Ast ast, GetSafeValueVisitor.SafeValueContext safeValueContext) { @@ -99,6 +100,13 @@ internal IsSafeValueVisitor(GetSafeValueVisitor.SafeValueContext safeValueContex public object VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst) { return false; } public object VisitBlockStatement(BlockStatementAst blockStatementAst) { return false; } public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { return false; } + public object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { return false; } + public object VisitPropertyMember(PropertyMemberAst propertyMemberAst) { return false; } + public object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return false; } + public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst) { return false; } + public object VisitUsingStatement(UsingStatementAst usingStatement) { return false; } + public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { return false; } + public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst) { return false; } public object VisitIndexExpression(IndexExpressionAst indexExpressionAst) { @@ -245,6 +253,12 @@ public object VisitMemberExpression(MemberExpressionAst memberExpressionAst) return false; } + public object VisitListExpression(ListExpressionAst listExpressionAst) + { + // A list expression *may* be safe, if its components are safe + return listExpressionAst.SubExpression.Accept(this); + } + public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) { // An Array expression *may* be safe, if its components are safe @@ -285,7 +299,7 @@ public object VisitParenExpression(ParenExpressionAst parenExpressionAst) * except in the case of handling the unary operator * ExecutionContext is provided to ensure we can resolve variables */ - internal class GetSafeValueVisitor : ICustomAstVisitor + internal class GetSafeValueVisitor : ICustomAstVisitor3 { internal enum SafeValueContext { @@ -349,6 +363,13 @@ public static object GetSafeValue(Ast ast, ExecutionContext context, SafeValueCo public object VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst) { throw PSTraceSource.NewArgumentException("ast"); } public object VisitBlockStatement(BlockStatementAst blockStatementAst) { throw PSTraceSource.NewArgumentException("ast"); } public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { throw PSTraceSource.NewArgumentException("ast"); } + public object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { throw PSTraceSource.NewArgumentException("ast"); } + public object VisitPropertyMember(PropertyMemberAst propertyMemberAst) { throw PSTraceSource.NewArgumentException("ast"); } + public object VisitFunctionMember(FunctionMemberAst functionMemberAst) { throw PSTraceSource.NewArgumentException("ast"); } + public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst) { throw PSTraceSource.NewArgumentException("ast"); } + public object VisitUsingStatement(UsingStatementAst usingStatement) { throw PSTraceSource.NewArgumentException("ast"); } + public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { throw PSTraceSource.NewArgumentException("ast"); } + public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst) { throw PSTraceSource.NewArgumentException("ast"); } // // This is similar to logic used deep in the engine for slicing something that can be sliced @@ -619,6 +640,12 @@ public object VisitMemberExpression(MemberExpressionAst memberExpressionAst) throw PSTraceSource.NewArgumentException("ast"); } + public object VisitListExpression(ListExpressionAst listExpressionAst) + { + // A list expression *may* be safe, if its components are safe + return new List((object[])listExpressionAst.SubExpression.Accept(this)); + } + public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) { // An Array expression *may* be safe, if its components are safe diff --git a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs index 7369607174a..0c7c80ac15c 100644 --- a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs +++ b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs @@ -418,7 +418,7 @@ private static bool IsConstructor(object member) } } - internal class TypeInferenceVisitor : ICustomAstVisitor2 + internal class TypeInferenceVisitor : ICustomAstVisitor3 { private readonly TypeInferenceContext _context; @@ -1575,6 +1575,11 @@ object ICustomAstVisitor2.VisitDynamicKeywordStatement(DynamicKeywordStatementAs return dynamicKeywordAst.CommandElements[0].Accept(this); } + object ICustomAstVisitor3.VisitListExpression(ListExpressionAst listExpressionAst) + { + return new[] { new PSTypeName(listExpressionAst.StaticType) }; + } + private static CommandBaseAst GetPreviousPipelineCommand(CommandAst commandAst) { var pipe = (PipelineAst)commandAst.Parent; diff --git a/src/System.Management.Automation/engine/parser/VariableAnalysis.cs b/src/System.Management.Automation/engine/parser/VariableAnalysis.cs index f89c3b83575..e057908a7a9 100644 --- a/src/System.Management.Automation/engine/parser/VariableAnalysis.cs +++ b/src/System.Management.Automation/engine/parser/VariableAnalysis.cs @@ -311,7 +311,7 @@ private void NoteVariable(string variableName, int index, Type type, bool automa } } - internal class VariableAnalysis : ICustomAstVisitor + internal class VariableAnalysis : ICustomAstVisitor3 { // Tuple slots start at index 0. >= 0 means a variable is allocated in the tuple. -1 means we haven't // analyzed a specific use of a variable and don't know what slot it might be assigned to yet. @@ -1732,6 +1732,12 @@ public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMember return null; } + public object VisitListExpression(ListExpressionAst listExpressionAst) + { + listExpressionAst.SubExpression.Accept(this); + return null; + } + public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) { arrayExpressionAst.SubExpression.Accept(this); @@ -1798,5 +1804,13 @@ public object VisitBlockStatement(BlockStatementAst blockStatementAst) blockStatementAst.Body.Accept(this); return null; } + + public object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { return null; } + public object VisitPropertyMember(PropertyMemberAst propertyMemberAst) { return null; } + public object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return null; } + public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst) { return null; } + public object VisitUsingStatement(UsingStatementAst usingStatement) { return null; } + public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { return null; } + public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst) { return null; } } } diff --git a/src/System.Management.Automation/engine/parser/ast.cs b/src/System.Management.Automation/engine/parser/ast.cs index 63dca76ca86..43e8137d558 100644 --- a/src/System.Management.Automation/engine/parser/ast.cs +++ b/src/System.Management.Automation/engine/parser/ast.cs @@ -9446,6 +9446,76 @@ internal override AstVisitAction InternalVisit(AstVisitor visitor) #endregion Visitors } + /// + /// The ast that represents a list expression, e.g. @[1]. + /// + public class ListExpressionAst : ExpressionAst + { + /// + /// Construct an expression that forces the result to be a list. + /// + /// The extent of the expression, including the opening '@[' and closing ']'. + /// The statements executed as part of the expression. + /// + /// If or is null. + /// + public ListExpressionAst(IScriptExtent extent, StatementBlockAst statementBlock) + : base(extent) + { + if (statementBlock == null) + { + throw PSTraceSource.NewArgumentNullException("statementBlock"); + } + + this.SubExpression = statementBlock; + SetParent(statementBlock); + } + + /// + /// The expression/statements represented by this sub-expression. + /// + public StatementBlockAst SubExpression { get; private set; } + + /// + /// Copy the ListExpressionAst instance + /// + public override Ast Copy() + { + var newStatementBlock = CopyElement(this.SubExpression); + return new ListExpressionAst(this.Extent, newStatementBlock); + } + + /// + /// The result of an ListExpressionAst is always typeof(List[object]). + /// + public override Type StaticType { get { return typeof(List); } } + + #region Visitors + + internal override object Accept(ICustomAstVisitor visitor) + { + var visitor3 = visitor as ICustomAstVisitor3; + return visitor3 != null ? visitor3.VisitListExpression(this) : null; + } + + internal override AstVisitAction InternalVisit(AstVisitor visitor) + { + var action = AstVisitAction.Continue; + var visitor3 = visitor as AstVisitor3; + if (visitor3 != null) + { + action = visitor3.VisitListExpression(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 + } + /// /// The ast that represents an array expression, e.g. @(1). The array literal (e.g. 1,2,3) is /// represented by . diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 6de70506924..ec6bd3fc574 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -812,6 +812,65 @@ public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObje } } + /// + /// This binder is used for the @[] operator. + /// + internal class PSToObjectListBinder : DynamicMetaObjectBinder + { + private static readonly PSToObjectListBinder s_binder = new PSToObjectListBinder(); + + internal static PSToObjectListBinder Get() + { + return s_binder; + } + + private PSToObjectListBinder() + { + } + + public override string ToString() + { + return "ToObjectList"; + } + + public override Type ReturnType { get { return typeof(List); } } + + public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) + { + if (!target.HasValue) + { + return Defer(target, args); + } + + if (target.Value == AutomationNull.Value) + { + return new DynamicMetaObject(ExpressionCache.NewEmptyObjectList, + BindingRestrictions.GetInstanceRestriction(target.Expression, AutomationNull.Value)).WriteToDebugLog(this); + } + + var enumerable = PSEnumerableBinder.IsEnumerable(target); + if (enumerable == null) + { + return new DynamicMetaObject( + Expression.ListInit(ExpressionCache.NewEmptyObjectList, CachedReflectionInfo.ObjectList_Add, target.Expression.Cast(typeof(object))), + target.PSGetTypeRestriction()).WriteToDebugLog(this); + } + + var value = PSObject.Base(target.Value); + if (value is IEnumerable) + { + return new DynamicMetaObject( + Expression.New(CachedReflectionInfo.ObjectList_IEnumerable_ctor, PSEnumerableBinder.MaybeDebase(this, e => e.Cast(typeof(IEnumerable)), target)), + PSEnumerableBinder.GetRestrictions(target)).WriteToDebugLog(this); + } + + // It's enumerable, but not an IEnumerable. Call EnumerableOps.ToArray + return new DynamicMetaObject( + Expression.Call(CachedReflectionInfo.EnumerableOps_ToList, enumerable.Expression), + target.PSGetTypeRestriction()).WriteToDebugLog(this); + } + } + internal class PSPipeWriterBinder : DynamicMetaObjectBinder { private static readonly PSPipeWriterBinder s_binder = new PSPipeWriterBinder(); diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index df5fc5f837d..e4964e2f8fa 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -3381,13 +3381,19 @@ internal static void WriteEnumerableToPipe(IEnumerator enumerator, Pipe pipe, Ex } } - internal static object[] ToArray(IEnumerator enumerator) + internal static List ToList(IEnumerator enumerator) { var result = new List(); while (MoveNext(null, enumerator)) { result.Add(Current(enumerator)); } + return result; + } + + internal static object[] ToArray(IEnumerator enumerator) + { + var result = ToList(enumerator); return result.ToArray(); } diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index 5f9b75345fe..de3609ca5cc 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -508,6 +508,9 @@ The correct form is: foreach ($a in $b) {...} Missing closing ')' in subexpression. + + Missing closing ']' in subexpression. + Missing '(' after '{0}' in if statement. From 6466f2b857df828d3750bd092d0c3ab87e5a5772 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Thu, 28 Dec 2017 13:20:31 -0800 Subject: [PATCH 2/3] ListExpression: parser changes --- .../engine/parser/Parser.cs | 48 ++++-- .../engine/parser/token.cs | 163 +++++++++--------- .../engine/parser/tokenizer.cs | 8 +- 3 files changed, 123 insertions(+), 96 deletions(-) diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index b347cbd3e03..0256b6de0f8 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -1776,7 +1776,8 @@ private IScriptExtent StatementListRule(List statements, List traps = new List(); List statements = new List(); - Token rParen; + + bool isListExpr = firstToken.Kind == TokenKind.AtBracket; + TokenKind expectedEndTokenKind = isListExpr ? TokenKind.RBracket : TokenKind.RParen; + Token endToken; bool oldDisableCommaOperator = _disableCommaOperator; var oldTokenizerMode = _tokenizer.Mode; @@ -6472,38 +6483,45 @@ private ExpressionAst SubExpressionRule(Token firstToken) { _disableCommaOperator = false; SetTokenizerMode(TokenizerMode.Command); + if (isListExpr) { _tokenizer.InListSubExpression = true; } SkipNewlines(); statementListExtent = StatementListRule(statements, traps); SkipNewlines(); - rParen = NextToken(); - if (rParen.Kind != TokenKind.RParen) + endToken = NextToken(); + if (endToken.Kind != expectedEndTokenKind) { - // ErrorRecovery: Assume only the closing paren is missing, continue as though it was present. + // ErrorRecovery: Assume only the closing paren or bracket is missing, continue as though it was present. - UngetToken(rParen); - ReportIncompleteInput(rParen.Extent, () => ParserStrings.MissingEndParenthesisInSubexpression); + UngetToken(endToken); + string errorString = isListExpr + ? ParserStrings.MissingEndBracketInSubexpression + : ParserStrings.MissingEndParenthesisInSubexpression; + ReportIncompleteInput(endToken.Extent, () => errorString); } } finally { _disableCommaOperator = oldDisableCommaOperator; SetTokenizerMode(oldTokenizerMode); + if (isListExpr) { _tokenizer.InListSubExpression = false; } } // End extent is rparen, end of the statement list (if no rparen), or the first token (if no statements). IScriptExtent extent = ExtentOf(firstToken, - rParen.Kind == TokenKind.RParen ? rParen.Extent : statementListExtent ?? firstToken.Extent); - if (firstToken.Kind == TokenKind.DollarParen) + endToken.Kind == expectedEndTokenKind ? endToken.Extent : statementListExtent ?? firstToken.Extent); + var statementBlock = new StatementBlockAst(statementListExtent ?? PositionUtilities.EmptyExtent, statements, traps); + if (isListExpr) + { + return new ListExpressionAst(extent, statementBlock); + } + else if (firstToken.Kind == TokenKind.DollarParen) { - return new SubExpressionAst(extent, - new StatementBlockAst(statementListExtent ?? PositionUtilities.EmptyExtent, - statements, traps)); + return new SubExpressionAst(extent, statementBlock); } - Diagnostics.Assert(firstToken.Kind == TokenKind.AtParen, "only support $() and @() here."); - return new ArrayExpressionAst(extent, - new StatementBlockAst(statementListExtent ?? PositionUtilities.EmptyExtent, statements, traps)); + Diagnostics.Assert(firstToken.Kind == TokenKind.AtParen, "only support @[], $() and @() here."); + return new ArrayExpressionAst(extent, statementBlock); } private ExpressionAst ParenthesizedExpressionRule(Token lParen) diff --git a/src/System.Management.Automation/engine/parser/token.cs b/src/System.Management.Automation/engine/parser/token.cs index 4d4940cc302..7a905e38161 100644 --- a/src/System.Management.Automation/engine/parser/token.cs +++ b/src/System.Management.Automation/engine/parser/token.cs @@ -145,274 +145,277 @@ public enum TokenKind /// The opening token of a hash expression '@{'. AtCurly = 23, + /// The opening token of a list expression '@['. + AtBracket = 24, + /// The opening token of a sub-expression '$('. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - DollarParen = 24, + DollarParen = 25, /// The statement terminator ';'. - Semi = 25, + Semi = 26, #endregion Punctuators #region Operators /// The (unimplemented) operator '&&'. - AndAnd = 26, + AndAnd = 27, /// The (unimplemented) operator '||'. - OrOr = 27, + OrOr = 28, /// The invocation operator '&'. - Ampersand = 28, + Ampersand = 29, /// The pipe operator '|'. - Pipe = 29, + Pipe = 30, /// The unary or binary array operator ','. - Comma = 30, + Comma = 31, /// The pre-decrement operator '--'. - MinusMinus = 31, + MinusMinus = 32, /// The pre-increment operator '++'. - PlusPlus = 32, + PlusPlus = 33, /// The range operator '..'. - DotDot = 33, + DotDot = 34, /// The static member access operator '::'. - ColonColon = 34, + ColonColon = 35, /// The instance member access or dot source invocation operator '.'. - Dot = 35, + Dot = 36, /// The logical not operator '!'. - Exclaim = 36, + Exclaim = 37, /// The multiplication operator '*'. - Multiply = 37, + Multiply = 38, /// The division operator '/'. - Divide = 38, + Divide = 39, /// The modulo division (remainder) operator '%'. - Rem = 39, + Rem = 40, /// The addition operator '+'. - Plus = 40, + Plus = 41, /// The substraction operator '-'. - Minus = 41, + Minus = 42, /// The assignment operator '='. - Equals = 42, + Equals = 43, /// The addition assignment operator '+='. - PlusEquals = 43, + PlusEquals = 44, /// The subtraction assignment operator '-='. - MinusEquals = 44, + MinusEquals = 45, /// The multiplication assignment operator '*='. - MultiplyEquals = 45, + MultiplyEquals = 46, /// The division assignment operator '/='. - DivideEquals = 46, + DivideEquals = 47, /// The modulo division (remainder) assignment operator '%='. - RemainderEquals = 47, + RemainderEquals = 48, /// A redirection operator such as '2>&1' or '>>'. - Redirection = 48, + Redirection = 49, /// The (unimplemented) stdin redirection operator '<'. - RedirectInStd = 49, + RedirectInStd = 50, /// The string format operator '-f'. - Format = 50, + Format = 51, /// The logical not operator '-not'. - Not = 51, + Not = 52, /// The bitwise not operator '-bnot'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Bnot = 52, + Bnot = 53, /// The logical and operator '-and'. - And = 53, + And = 54, /// The logical or operator '-or'. - Or = 54, + Or = 55, /// The logical exclusive or operator '-xor'. - Xor = 55, + Xor = 56, /// The bitwise and operator '-band'. - Band = 56, + Band = 57, /// The bitwise or operator '-bor'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Bor = 57, + Bor = 58, /// The bitwise exclusive or operator '-xor'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Bxor = 58, + Bxor = 59, /// The join operator '-join'. - Join = 59, + Join = 60, /// The case insensitive equal operator '-ieq' or '-eq'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Ieq = 60, + Ieq = 61, /// The case insensitive not equal operator '-ine' or '-ne'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Ine = 61, + Ine = 62, /// The case insensitive greater than or equal operator '-ige' or '-ge'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Ige = 62, + Ige = 63, /// The case insensitive greater than operator '-igt' or '-gt'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Igt = 63, + Igt = 64, /// The case insensitive less than operator '-ilt' or '-lt'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Ilt = 64, + Ilt = 65, /// The case insensitive less than or equal operator '-ile' or '-le'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Ile = 65, + Ile = 66, /// The case insensitive like operator '-ilike' or '-like'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Ilike = 66, + Ilike = 67, /// The case insensitive not like operator '-inotlike' or '-notlike'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Inotlike = 67, + Inotlike = 68, /// The case insensitive match operator '-imatch' or '-match'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Imatch = 68, + Imatch = 69, /// The case insensitive not match operator '-inotmatch' or '-notmatch'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Inotmatch = 69, + Inotmatch = 70, /// The case insensitive replace operator '-ireplace' or '-replace'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Ireplace = 70, + Ireplace = 71, /// The case insensitive contains operator '-icontains' or '-contains'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Icontains = 71, + Icontains = 72, /// The case insensitive notcontains operator '-inotcontains' or '-notcontains'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Inotcontains = 72, + Inotcontains = 73, /// The case insensitive in operator '-iin' or '-in'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Iin = 73, + Iin = 74, /// The case insensitive notin operator '-inotin' or '-notin' [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Inotin = 74, + Inotin = 75, /// The case insensitive split operator '-isplit' or '-split'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Isplit = 75, + Isplit = 76, /// The case sensitive equal operator '-ceq'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Ceq = 76, + Ceq = 77, /// The case sensitive not equal operator '-cne'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Cne = 77, + Cne = 78, /// The case sensitive greater than or equal operator '-cge'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Cge = 78, + Cge = 79, /// The case sensitive greater than operator '-cgt'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Cgt = 79, + Cgt = 80, /// The case sensitive less than operator '-clt'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Clt = 80, + Clt = 81, /// The case sensitive less than or equal operator '-cle'. - Cle = 81, + Cle = 82, /// The case sensitive like operator '-clike'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Clike = 82, + Clike = 83, /// The case sensitive notlike operator '-cnotlike'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Cnotlike = 83, + Cnotlike = 84, /// The case sensitive match operator '-cmatch'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Cmatch = 84, + Cmatch = 85, /// The case sensitive not match operator '-cnotmatch'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Cnotmatch = 85, + Cnotmatch = 86, /// The case sensitive replace operator '-creplace'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Creplace = 86, + Creplace = 87, /// The case sensitive contains operator '-ccontains'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Ccontains = 87, + Ccontains = 88, /// The case sensitive not contains operator '-cnotcontains'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Cnotcontains = 88, + Cnotcontains = 89, /// The case sensitive in operator '-cin'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Cin = 89, + Cin = 90, /// The case sensitive not in operator '-notin'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Cnotin = 90, + Cnotin = 91, /// The case sensitive split operator '-csplit'. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Csplit = 91, + Csplit = 92, /// The type test operator '-is'. - Is = 92, + Is = 93, /// The type test operator '-isnot'. - IsNot = 93, + IsNot = 94, /// The type conversion operator '-as'. - As = 94, + As = 95, /// The post-increment operator '++'. - PostfixPlusPlus = 95, + PostfixPlusPlus = 96, /// The post-decrement operator '--'. - PostfixMinusMinus = 96, + PostfixMinusMinus = 97, /// The shift left operator. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Shl = 97, + Shl = 98, /// The shift right operator. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Shr = 98, + Shr = 99, /// The PS class base class and implemented interfaces operator ':'. Also used in base class ctor calls. - Colon = 99, + Colon = 100, #endregion Operators @@ -767,6 +770,7 @@ public static class TokenTraits /* RBracket */ TokenFlags.ParseModeInvariant, /* AtParen */ TokenFlags.ParseModeInvariant, /* AtCurly */ TokenFlags.ParseModeInvariant, + /* AtBracket */ TokenFlags.ParseModeInvariant, /* DollarParen */ TokenFlags.ParseModeInvariant, /* Semi */ TokenFlags.ParseModeInvariant, @@ -848,6 +852,7 @@ public static class TokenTraits /* Shl */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold, /* Shr */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold, /* Colon */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode, + /* Reserved slot 1 */ TokenFlags.None, /* Reserved slot 2 */ TokenFlags.None, /* Reserved slot 3 */ TokenFlags.None, /* Reserved slot 4 */ TokenFlags.None, @@ -865,8 +870,6 @@ public static class TokenTraits /* Reserved slot 16 */ TokenFlags.None, /* Reserved slot 17 */ TokenFlags.None, /* Reserved slot 18 */ TokenFlags.None, - /* Reserved slot 19 */ TokenFlags.None, - /* Reserved slot 20 */ TokenFlags.None, #endregion Flags for operators @@ -965,6 +968,7 @@ public static class TokenTraits /* RBracket */ "]", /* AtParen */ "@(", /* AtCurly */ "@{", + /* AtBracket */ "@[", /* DollarParen */ "$(", /* Semi */ ";", @@ -1046,6 +1050,7 @@ public static class TokenTraits /* Shl */ "-shl", /* Shr */ "-shr", /* Colon */ ":", + /* Reserved slot 1 */ "", /* Reserved slot 2 */ "", /* Reserved slot 3 */ "", /* Reserved slot 4 */ "", @@ -1063,8 +1068,6 @@ public static class TokenTraits /* Reserved slot 16 */ "", /* Reserved slot 17 */ "", /* Reserved slot 18 */ "", - /* Reserved slot 19 */ "", - /* Reserved slot 20 */ "", #endregion Text for operators diff --git a/src/System.Management.Automation/engine/parser/tokenizer.cs b/src/System.Management.Automation/engine/parser/tokenizer.cs index c951d1f996a..05b32ba013f 100644 --- a/src/System.Management.Automation/engine/parser/tokenizer.cs +++ b/src/System.Management.Automation/engine/parser/tokenizer.cs @@ -637,6 +637,7 @@ internal Tokenizer(Parser parser) internal bool AllowSignedNumbers { get; set; } internal bool WantSimpleName { get; set; } internal bool InWorkflowContext { get; set; } + internal bool InListSubExpression { get; set; } internal List TokenList { get; set; } internal Token FirstToken { get; private set; } internal Token LastToken { get; private set; } @@ -3003,7 +3004,8 @@ private Token ScanGenericToken(StringBuilder sb) formatSb.Append(sb); char c = GetChar(); - for (; !c.ForceStartNewToken(); c = GetChar()) + // In a list expression @[], don't take a closing bracket in a generic token + for (; !c.ForceStartNewToken() && !(c == ']' && InListSubExpression); c = GetChar()) { if (c == '`') { @@ -3886,6 +3888,10 @@ internal Token NextToken() { return NewToken(TokenKind.AtParen); } + if (c1 == '[') + { + return NewToken(TokenKind.AtBracket); + } if (c1.IsSingleQuote()) { return ScanHereStringLiteral(); From 3182947fe57f33ba464a66e6cf3934d160fa9639 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 29 Dec 2017 11:50:31 -0800 Subject: [PATCH 3/3] ListExpression: minor optimization to 'CompileListInitExpression' --- .../engine/interpreter/ArrayOperations.cs | 36 +++++++++++++++++++ .../engine/interpreter/InstructionFactory.cs | 16 +++++++++ .../engine/interpreter/InstructionList.cs | 14 ++++++++ .../engine/interpreter/LightCompiler.cs | 18 ++++++++++ 4 files changed, 84 insertions(+) diff --git a/src/System.Management.Automation/engine/interpreter/ArrayOperations.cs b/src/System.Management.Automation/engine/interpreter/ArrayOperations.cs index f85482522c5..ce0ddc65e80 100644 --- a/src/System.Management.Automation/engine/interpreter/ArrayOperations.cs +++ b/src/System.Management.Automation/engine/interpreter/ArrayOperations.cs @@ -13,6 +13,8 @@ * * ***************************************************************************/ +using System.Collections.Generic; + namespace System.Management.Automation.Interpreter { internal sealed class NewArrayInitInstruction : Instruction @@ -39,6 +41,40 @@ public override int Run(InterpretedFrame frame) } } + internal sealed class NewListInitInstruction : 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 list; + if (_elementCount == 1) + { + list = new List() { (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(array); + } + + frame.Push(list); + return +1; + } + } + internal sealed class NewArrayInstruction : Instruction { internal NewArrayInstruction() { } diff --git a/src/System.Management.Automation/engine/interpreter/InstructionFactory.cs b/src/System.Management.Automation/engine/interpreter/InstructionFactory.cs index 98e8545277a..f370a85f3d3 100644 --- a/src/System.Management.Automation/engine/interpreter/InstructionFactory.cs +++ b/src/System.Management.Automation/engine/interpreter/InstructionFactory.cs @@ -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 : InstructionFactory @@ -75,6 +76,7 @@ internal sealed class InstructionFactory : 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; @@ -124,5 +126,19 @@ protected internal override Instruction NewArrayInit(int elementCount) } return new NewArrayInitInstruction(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(elementCount)); + } + return new NewListInitInstruction(elementCount); + } } } diff --git a/src/System.Management.Automation/engine/interpreter/InstructionList.cs b/src/System.Management.Automation/engine/interpreter/InstructionList.cs index cddf8dab6d8..128a9a1d311 100644 --- a/src/System.Management.Automation/engine/interpreter/InstructionList.cs +++ b/src/System.Management.Automation/engine/interpreter/InstructionList.cs @@ -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.Factory.NewListInit(elementCount)); + } + else + { + Emit(InstructionFactory.GetFactory(elementType).NewListInit(elementCount)); + } + } + #endregion #region Arithmetic Operations diff --git a/src/System.Management.Automation/engine/interpreter/LightCompiler.cs b/src/System.Management.Automation/engine/interpreter/LightCompiler.cs index 12450c12945..ec49202b594 100644 --- a/src/System.Management.Automation/engine/interpreter/LightCompiler.cs +++ b/src/System.Management.Automation/engine/interpreter/LightCompiler.cs @@ -1804,6 +1804,24 @@ private void CompileInvocationExpression(Expression expr) private void CompileListInitExpression(Expression expr) { var node = (ListInitExpression) expr; +#if !DEBUG + // Optimize the most common use -- create and initialize an object list + // - Use the default constructor of List + // - 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) {