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
83 changes: 83 additions & 0 deletions src/System.Management.Automation/engine/lang/parserutils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ static ParserOps()
internal static readonly object _TrueObject = (object)true;
internal static readonly object _FalseObject = (object)false;

private static readonly CallSite<Func<CallSite, object, bool>> _toBoolSite = CallSite<Func<CallSite, object, bool>>.Create(PSConvertBinder.Get(typeof(bool)));

internal static string CharToString(char ch)
{
if (ch < 255) return s_chars[ch];
Expand Down Expand Up @@ -591,6 +593,87 @@ private static void ExtendList<T>(IList<T> list, IList<T> items)
}
}

internal static bool AnyOperator(ExecutionContext context,
CallSite<Func<CallSite, object, IEnumerator>> getEnumeratorSite,
CallSite<Func<CallSite, object, object, object>> comparerSite,
object left,
object right)
{
ScriptBlock predicate = right as ScriptBlock;
if (predicate == null)
{
return ContainsOperatorCompiled(context, getEnumeratorSite, comparerSite, left, right);
throw InterpreterError.NewInterpreterException(right, typeof(RuntimeException), new ScriptExtent(new ScriptPosition("", 0, 0, ""), new ScriptPosition("", 0, 0, "")),
"BadOperatorArgument", ParserStrings.BadOperatorArgument, "-any", right);
}

IEnumerator list = getEnumeratorSite.Target.Invoke(getEnumeratorSite, left);
if (list == null || list is EnumerableOps.NonEnumerableObjectEnumerator)
{
object result = predicate.DoInvokeReturnAsIs(true, ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, left, null, null, null);
return _toBoolSite.Target.Invoke(_toBoolSite, result);
}

while (EnumerableOps.MoveNext(context, list))
{
object val = EnumerableOps.Current(list);
object result = predicate.DoInvokeReturnAsIs(true, ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, val, null, null, null);
if (_toBoolSite.Target.Invoke(_toBoolSite, result))
{
return true;
}
}

return false;
}

internal static bool AllOperator(ExecutionContext context,
CallSite<Func<CallSite, object, IEnumerator>> getEnumeratorSite,
CallSite<Func<CallSite, object, object, object>> comparerSite,
object left,
object right)
{
IEnumerator list = getEnumeratorSite.Target.Invoke(getEnumeratorSite, left);

ScriptBlock predicate = right as ScriptBlock;
if (predicate == null)
{
if (list == null || list is EnumerableOps.NonEnumerableObjectEnumerator)
{
return (bool)comparerSite.Target.Invoke(comparerSite, left, right);
}

while (EnumerableOps.MoveNext(context, list))
{
object val = EnumerableOps.Current(list);
if (!_toBoolSite.Target.Invoke(_toBoolSite, comparerSite.Target.Invoke(comparerSite, val, right)))
{
return false;
}
}

return true;
}

if (list == null || list is EnumerableOps.NonEnumerableObjectEnumerator)
{
object result = predicate.DoInvokeReturnAsIs(true, ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, left, null, null, null);
return _toBoolSite.Target.Invoke(_toBoolSite, result);
}

while (EnumerableOps.MoveNext(context, list))
{
object val = EnumerableOps.Current(list);
object result = predicate.DoInvokeReturnAsIs(true, ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, val, null, null, null);
if (!_toBoolSite.Target.Invoke(_toBoolSite, result))
{
return false;
}
}

return true;
}

private static object SplitOperatorImpl(ExecutionContext context, IScriptExtent errorPosition, object lval, object rval, SplitImplOptions implOptions, bool ignoreCase)
{
IEnumerable<string> content = enumerateContent(context, errorPosition, implOptions, lval);
Expand Down
30 changes: 30 additions & 0 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ internal static class CachedReflectionInfo
typeof(ParserOps).GetMethod(nameof(ParserOps.UnaryJoinOperator), staticFlags);
internal static readonly MethodInfo ParserOps_UnarySplitOperator =
typeof(ParserOps).GetMethod(nameof(ParserOps.UnarySplitOperator), staticFlags);
internal static readonly MethodInfo ParserOps_AnyOperator =
typeof(ParserOps).GetMethod(nameof(ParserOps.AnyOperator), staticFlags);
internal static readonly MethodInfo ParserOps_AllOperator =
typeof(ParserOps).GetMethod(nameof(ParserOps.AllOperator), staticFlags);

internal static readonly ConstructorInfo Pipe_ctor =
typeof(Pipe).GetConstructor(instanceFlags, null, CallingConventions.Standard, new Type[] { typeof(List<object>) }, null);
Expand Down Expand Up @@ -5008,6 +5012,28 @@ public Expression GenerateCallContains(Expression lhs, Expression rhs, bool igno
rhs.Cast(typeof(object)));
}

public Expression GenerateCallAny(Expression lhs, Expression rhs, bool ignoreCase)
{
return Expression.Call(
CachedReflectionInfo.ParserOps_AnyOperator,
_executionContextParameter,
Expression.Constant(CallSite<Func<CallSite, object, IEnumerator>>.Create(PSEnumerableBinder.Get())),
Expression.Constant(CallSite<Func<CallSite, object, object, object>>.Create(PSBinaryOperationBinder.Get(ExpressionType.Equal, ignoreCase, true))),
lhs.Cast(typeof(object)),
rhs.Cast(typeof(object)));
}

public Expression GenerateCallAll(Expression lhs, Expression rhs, bool ignoreCase)
{
return Expression.Call(
CachedReflectionInfo.ParserOps_AllOperator,
_executionContextParameter,
Expression.Constant(CallSite<Func<CallSite, object, IEnumerator>>.Create(PSEnumerableBinder.Get())),
Expression.Constant(CallSite<Func<CallSite, object, object, object>>.Create(PSBinaryOperationBinder.Get(ExpressionType.Equal, ignoreCase, true))),
lhs.Cast(typeof(object)),
rhs.Cast(typeof(object)));
}

public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)
{
object constantValue;
Expand Down Expand Up @@ -5179,6 +5205,10 @@ public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)
return GenerateCallContains(lhs, rhs, true);
case TokenKind.Inotcontains:
return Expression.Not(GenerateCallContains(lhs, rhs, true));
case TokenKind.Any:
return GenerateCallAny(lhs, rhs, true);
case TokenKind.All:
return GenerateCallAll(lhs, rhs, true);
case TokenKind.Iin:
return GenerateCallContains(rhs, lhs, true);
case TokenKind.Inotin:
Expand Down
15 changes: 10 additions & 5 deletions src/System.Management.Automation/engine/parser/token.cs
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@ public enum TokenKind
/// <summary>The PS class base class and implemented interfaces operator ':'. Also used in base class ctor calls.</summary>
Colon = 99,

/// <summary>Any operator '-any'</summary>
Any = 100,

/// <summary>All operator '-all'</summary>
All = 101,
#endregion Operators

#region Keywords
Expand Down Expand Up @@ -598,7 +603,7 @@ public enum TokenFlags

/// <summary>
/// The precedence of comparison operators including: '-eq', '-ne', '-ge', '-gt', '-lt', '-le', '-like', '-notlike',
/// '-match', '-notmatch', '-replace', '-contains', '-notcontains', '-in', '-notin', '-split', '-join', '-is', '-isnot', '-as',
/// '-match', '-notmatch', '-replace', '-contains', '-notcontains', '-in', '-notin', '-split', '-join', '-is', '-isnot', '-as', '-any', '-all',
/// and all of the case sensitive variants of these operators, if they exists.
/// </summary>
BinaryPrecedenceComparison = 3,
Expand Down Expand Up @@ -847,8 +852,8 @@ 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 2 */ TokenFlags.None,
/* Reserved slot 3 */ TokenFlags.None,
/* Any */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
/* All */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
/* Reserved slot 4 */ TokenFlags.None,
/* Reserved slot 5 */ TokenFlags.None,
/* Reserved slot 6 */ TokenFlags.None,
Expand Down Expand Up @@ -1045,8 +1050,8 @@ public static class TokenTraits
/* Shl */ "-shl",
/* Shr */ "-shr",
/* Colon */ ":",
/* Reserved slot 2 */ string.Empty,
/* Reserved slot 3 */ string.Empty,
/* Any */ "-any",
/* All */ "-all",
/* Reserved slot 4 */ string.Empty,
/* Reserved slot 5 */ string.Empty,
/* Reserved slot 6 */ string.Empty,
Expand Down
6 changes: 4 additions & 2 deletions src/System.Management.Automation/engine/parser/tokenizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,8 @@ private static readonly Dictionary<string, TokenKind> s_operatorTable
/*13*/ "isplit", "csplit", "isnot", "is", /*13*/
/*14*/ "as", "f", "and", "band", /*14*/
/*15*/ "or", "bor", "xor", "bxor", /*15*/
/*16*/ "join", "shl", "shr", /*16*/
/*16*/ "join", "shl", "shr", "any", /*16*/
/*17*/ "all", /*17*/
};

private static readonly TokenKind[] s_operatorTokenKind = new TokenKind[] {
Expand All @@ -656,7 +657,8 @@ private static readonly Dictionary<string, TokenKind> s_operatorTable
/*13*/ TokenKind.Isplit, TokenKind.Csplit, TokenKind.IsNot, TokenKind.Is, /*13*/
/*14*/ TokenKind.As, TokenKind.Format, TokenKind.And, TokenKind.Band, /*14*/
/*15*/ TokenKind.Or, TokenKind.Bor, TokenKind.Xor, TokenKind.Bxor, /*15*/
/*16*/ TokenKind.Join, TokenKind.Shl, TokenKind.Shr, /*16*/
/*16*/ TokenKind.Join, TokenKind.Shl, TokenKind.Shr, TokenKind.Any, /*16*/
/*17*/ TokenKind.All, /*17*/
};

#endregion Tables for initialization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,10 @@
<data name="shrOperatorDescription" xml:space="preserve">
<value>Shift Right bit operator. Inserts zero in the left-most bit position. For signed values, sign bit is preserved.</value>
</data>
<data name="allOperatorDescription" xml:space="preserve">
<value>All operator. Returns TRUE when all of the left hand values are TRUE for the right hand predicate</value>
</data>
<data name="anyOperatorDescription" xml:space="preserve">
<value>Any operator. Returns TRUE when any of the left hand values are TRUE for the right hand predicate</value>
</data>
</root>