diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 9c3175fa8e1..4bbdea688c0 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -38,6 +38,18 @@ internal static class CachedReflectionInfo internal static readonly MethodInfo ObjectList_ToArray = typeof(List).GetMethod(nameof(List.ToArray), PSTypeExtensions.EmptyTypes); + internal static readonly MethodInfo ArrayOps_AddAssignArray = + typeof(ArrayOps).GetMethod(nameof(ArrayOps.AddAssignArray), staticFlags); + // internal static readonly MethodInfo ArrayOps_AddAssignArrayDefault = + // typeof(ArrayOps).GetMethod(nameof(ArrayOps.AddAssignArrayDefault), staticFlags); + internal static readonly MethodInfo ArrayOps_AddAssignEnumerable = + typeof(ArrayOps).GetMethod(nameof(ArrayOps.AddAssignEnumerable), staticFlags); + // internal static readonly MethodInfo ArrayOps_AddAssignEnumerableDefault = + // typeof(ArrayOps).GetMethod(nameof(ArrayOps.AddAssignEnumerableDefault), staticFlags); + internal static readonly MethodInfo ArrayOps_AddAssignObject = + typeof(ArrayOps).GetMethod(nameof(ArrayOps.AddAssignObject), staticFlags); + // internal static readonly MethodInfo ArrayOps_AddAssignObjectDefault = + // typeof(ArrayOps).GetMethod(nameof(ArrayOps.AddAssignObjectDefault), staticFlags); internal static readonly MethodInfo ArrayOps_GetMDArrayValue = typeof(ArrayOps).GetMethod(nameof(ArrayOps.GetMDArrayValue), staticFlags); internal static readonly MethodInfo ArrayOps_GetMDArrayValueOrSlice = @@ -818,7 +830,7 @@ internal Expression ReduceAssignment(ISupportsAssignment left, TokenKind tokenKi switch (tokenKind) { case TokenKind.Equals: return av.SetValue(this, right); - case TokenKind.PlusEquals: et = ExpressionType.Add; break; + case TokenKind.PlusEquals: et = (left is Array) ? ExpressionType.AddAssign : ExpressionType.Add; break; case TokenKind.MinusEquals: et = ExpressionType.Subtract; break; case TokenKind.MultiplyEquals: et = ExpressionType.Multiply; break; case TokenKind.DivideEquals: et = ExpressionType.Divide; break; @@ -6058,4 +6070,4 @@ public void AddInstructions(LightCompiler compiler) compiler.Instructions.Emit(UpdatePositionInstruction.Create(_sequencePoint, _checkBreakpoints)); } } -} +} \ No newline at end of file diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 536350cc661..10d9a5c2e53 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -2164,6 +2164,8 @@ public override DynamicMetaObject FallbackBinaryOperation(DynamicMetaObject targ return BinaryDivide(target, arg, errorSuggestion).WriteToDebugLog(this); case ExpressionType.Modulo: return BinaryRemainder(target, arg, errorSuggestion).WriteToDebugLog(this); + case ExpressionType.AddAssign: + return BinaryAddAssign(target, arg, errorSuggestion).WriteToDebugLog(this); case ExpressionType.And: return BinaryBitwiseAnd(target, arg, errorSuggestion).WriteToDebugLog(this); case ExpressionType.Or: @@ -2222,6 +2224,7 @@ private string GetOperatorText() case ExpressionType.Multiply: return TokenKind.Multiply.Text(); case ExpressionType.Divide: return TokenKind.Divide.Text(); case ExpressionType.Modulo: return TokenKind.Rem.Text(); + case ExpressionType.AddAssign: return TokenKind.PlusEquals.Text(); case ExpressionType.And: return TokenKind.Band.Text(); case ExpressionType.Or: return TokenKind.Bor.Text(); case ExpressionType.ExclusiveOr: return TokenKind.Bxor.Text(); @@ -2769,6 +2772,108 @@ private DynamicMetaObject BinaryRemainder(DynamicMetaObject target, DynamicMetaO return BinarySubDivOrRem(target, arg, errorSuggestion, "Remainder", "op_Modulus", "%"); } + private DynamicMetaObject BinaryAddAssign(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) + { + if ( target.Value is Array) + { + var lhsEnumerator = PSEnumerableBinder.IsEnumerable(target); + Type targetElementType = target.Value.GetType().GetElementType(); + + if (arg.Value is Array) + { + if (arg.Value.GetType().GetElementType().IsAssignableFrom(targetElementType)) + { + // TargetArray += LikeArray > TargetArrayType + return new DynamicMetaObject(Expression.Call( + CachedReflectionInfo.ArrayOps_AddAssignArray, + target.Expression.Cast(typeof(Array)), + arg.Expression.Cast(typeof(Array)), + Expression.Constant(targetElementType)), + target.CombineRestrictions(arg)); + } + else + { + var e = (IEnumerator)arg.Expression; + while (e.MoveNext()) + { + if ( ! ( e.Current.GetType().GetElementType().IsAssignableFrom(targetElementType) ) ) + { + // TargetArray += UnlikeArrayUnlikeElements > Object[] + return new DynamicMetaObject(Expression.Call( + CachedReflectionInfo.ArrayOps_AddAssignArray, + target.Expression.Cast(typeof(Array)), + arg.Expression.Cast(typeof(Array)), + Expression.Constant(typeof(Object))), + target.CombineRestrictions(arg)); + } + } + // TargetArray += UnlikeArraylikeElements > TargetArrayType + return new DynamicMetaObject(Expression.Call( + CachedReflectionInfo.ArrayOps_AddAssignArray, + target.Expression.Cast(typeof(Array)), + arg.Expression.Cast(typeof(Array)), + Expression.Constant(targetElementType)), + target.CombineRestrictions(arg)); + } + } + + var rhsEnumerator = PSEnumerableBinder.IsEnumerable(arg); + if (rhsEnumerator != null) + { + var e = (IEnumerator)arg.Expression; + while (e.MoveNext()) + { + if ( ! ( e.Current.GetType().GetElementType().IsAssignableFrom(targetElementType) ) ) + { + // TargetArray += EnumerableUnlikeElements > Object[] + return new DynamicMetaObject(Expression.Call( + CachedReflectionInfo.ArrayOps_AddAssignEnumerable, + ExpressionCache.GetExecutionContextFromTLS, + target.Expression.Cast(typeof(Array)), + arg.Expression.Cast(typeof(IEnumerator)), + Expression.Constant(typeof(Object))), + target.CombineRestrictions(arg)); + } + } + // TargetArray += EnumerableLikeElements > TargetArrayType + return new DynamicMetaObject(Expression.Call( + CachedReflectionInfo.ArrayOps_AddAssignEnumerable, + ExpressionCache.GetExecutionContextFromTLS, + target.Expression.Cast(typeof(Array)), + arg.Expression.Cast(typeof(IEnumerator)), + Expression.Constant(targetElementType)), + target.CombineRestrictions(arg)); + } + + if (arg.Value.GetType().GetElementType().IsAssignableFrom(targetElementType) ) + { + // TargetArray += LikeObject > TargetArrayType + return new DynamicMetaObject(Expression.Call( + CachedReflectionInfo.ArrayOps_AddAssignObject, + target.Expression.Cast(typeof(Array)), + arg.Expression.Cast(typeof(object)), + Expression.Constant(targetElementType)), + target.CombineRestrictions(arg)); + } + + // TargetArray += UnlikeObject > Object[] + return new DynamicMetaObject(Expression.Call( + CachedReflectionInfo.ArrayOps_AddAssignObject, + // ExpressionCache.GetExecutionContextFromTLS, + target.Expression.Cast(typeof(Array)), + arg.Expression.Cast(typeof(object)), + Expression.Constant(typeof(Object))), + target.CombineRestrictions(arg)); + } + else + { + return (errorSuggestion ?? + new DynamicMetaObject( + Compiler.CreateThrow(typeof(object), typeof(PSNotImplementedException), "Unimplemented operation"), + target.CombineRestrictions(arg))).WriteToDebugLog(this); + } + } + private DynamicMetaObject BinarySubDivOrRem(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion, @@ -7413,4 +7518,4 @@ public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, Dynam } #endregion Standard binders -} +} \ No newline at end of file diff --git a/src/System.Management.Automation/engine/runtime/Operations/ArrayOps.cs b/src/System.Management.Automation/engine/runtime/Operations/ArrayOps.cs index 5c44e281e28..7f18352b01f 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/ArrayOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/ArrayOps.cs @@ -4,6 +4,7 @@ // ReSharper disable UnusedMember.Global +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Management.Automation.Internal; @@ -97,6 +98,83 @@ internal static T[] Multiply(T[] array, uint times) return result; } + internal static object AddAssignArray(Array target, Array arg, Type elementType) + { + Array result = Array.CreateInstance(elementType, target.Length + arg.Length); + target.CopyTo(result, 0); + arg.CopyTo(result, target.Length); + + return result; + } + + // internal static object AddAssignArrayDefault(Array target, Array arg) + // { + // int newSize = target.Length + arg.Length; + // object[] result = new object[newSize]; + + // target.CopyTo(result, 0); + // arg.CopyTo(result, target.Length); + + // return result; + // } + + internal static object AddAssignEnumerable(ExecutionContext context, Array target, IEnumerator arg, Type elementType) + { +// var fakeEnumerator = arg as NonEnumerableObjectEnumerator; + + var argAsList = new List(); + + while (EnumerableOps.MoveNext(context, arg)) + { + argAsList.Add(EnumerableOps.Current(arg)); + } + + Array result = Array.CreateInstance(elementType, target.Length + argAsList.Count); + target.CopyTo(result, 0); + argAsList.ToArray().CopyTo(result, target.Length); + + return result; + } + +// internal static object AddAssignEnumerableDefault(ExecutionContext context, Array target, IEnumerator arg) +// { +// // var fakeEnumerator = arg as NonEnumerableObjectEnumerator; +// var argAsList = new List(); + +// while (EnumerableOps.MoveNext(context, arg)) +// { +// argAsList.Add(EnumerableOps.Current(arg)); +// } + +// int newSize = target.Length + argAsList.Count; +// object[] result = new object[newSize]; + +// target.CopyTo(result, 0); +// argAsList.ToArray().CopyTo(result, target.Length); + +// return result; +// } + + internal static object AddAssignObject(Array target, object arg, Type elementType) + { + Array result = Array.CreateInstance(elementType, target.Length + 1); + target.CopyTo(result, 0); + target.SetValue(arg, target.Length); + + return target; + } + + // internal static object AddAssignObjectDefault(Array target, object arg) + // { + // int newSize = target.Length + 1; + // object[] result = new object[newSize]; + + // target.CopyTo(result, 0); + // result.SetValue(arg, target.Length); + + // return result; + // } + internal static object GetMDArrayValue(Array array, int[] indexes, bool slicing) { if (array.Rank != indexes.Length)