File: System\Linq\Expressions\Interpreter\ArrayOperations.cs
Web Access
Project: src\src\libraries\System.Linq.Expressions\src\System.Linq.Expressions.csproj (System.Linq.Expressions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics.CodeAnalysis;
 
namespace System.Linq.Expressions.Interpreter
{
    [RequiresDynamicCode(Expression.NewArrayRequiresDynamicCode)]
    internal sealed class NewArrayInitInstruction : Instruction
    {
        private readonly Type _elementType;
        private readonly int _elementCount;
 
        internal NewArrayInitInstruction(Type elementType, int elementCount)
        {
            _elementType = elementType;
            _elementCount = elementCount;
        }
 
        public override int ConsumedStack => _elementCount;
        public override int ProducedStack => 1;
        public override string InstructionName => "NewArrayInit";
 
        public override int Run(InterpretedFrame frame)
        {
            Array array = Array.CreateInstance(_elementType, _elementCount);
            for (int i = _elementCount - 1; i >= 0; i--)
            {
                array.SetValue(frame.Pop(), i);
            }
            frame.Push(array);
            return 1;
        }
    }
 
    [RequiresDynamicCode(Expression.NewArrayRequiresDynamicCode)]
    internal sealed class NewArrayInstruction : Instruction
    {
        private readonly Type _elementType;
 
        internal NewArrayInstruction(Type elementType)
        {
            _elementType = elementType;
        }
 
        public override int ConsumedStack => 1;
        public override int ProducedStack => 1;
        public override string InstructionName => "NewArray";
 
        public override int Run(InterpretedFrame frame)
        {
            int length = ConvertHelper.ToInt32NoNull(frame.Pop());
            // To make behavior aligned with array creation emitted by C# compiler if length is less than
            // zero we try to use it to create an array, which will throw an OverflowException with the
            // correct localized error message.
            frame.Push(length < 0 ? new int[length] : Array.CreateInstance(_elementType, length));
            return 1;
        }
    }
 
    [RequiresDynamicCode(Expression.NewArrayRequiresDynamicCode)]
    internal sealed class NewArrayBoundsInstruction : Instruction
    {
        private readonly Type _elementType;
        private readonly int _rank;
 
        internal NewArrayBoundsInstruction(Type elementType, int rank)
        {
            _elementType = elementType;
            _rank = rank;
        }
 
        public override int ConsumedStack => _rank;
        public override int ProducedStack => 1;
        public override string InstructionName => "NewArrayBounds";
 
        public override int Run(InterpretedFrame frame)
        {
            var lengths = new int[_rank];
            for (int i = _rank - 1; i >= 0; i--)
            {
                int length = ConvertHelper.ToInt32NoNull(frame.Pop());
 
                if (length < 0)
                {
                    // to make behavior aligned with array creation emitted by C# compiler
                    throw new OverflowException();
                }
 
                lengths[i] = length;
            }
            Array array = Array.CreateInstance(_elementType, lengths);
            frame.Push(array);
            return 1;
        }
    }
 
    internal sealed class GetArrayItemInstruction : Instruction
    {
        internal static readonly GetArrayItemInstruction Instance = new GetArrayItemInstruction();
 
        private GetArrayItemInstruction() { }
 
        public override int ConsumedStack => 2;
        public override int ProducedStack => 1;
        public override string InstructionName => "GetArrayItem";
 
        public override int Run(InterpretedFrame frame)
        {
            int index = ConvertHelper.ToInt32NoNull(frame.Pop());
            Array array = (Array)frame.Pop()!;
            frame.Push(array.GetValue(index));
            return 1;
        }
    }
 
    internal sealed class SetArrayItemInstruction : Instruction
    {
        internal static readonly SetArrayItemInstruction Instance = new SetArrayItemInstruction();
 
        private SetArrayItemInstruction() { }
 
        public override int ConsumedStack => 3;
        public override string InstructionName => "SetArrayItem";
 
        public override int Run(InterpretedFrame frame)
        {
            object? value = frame.Pop();
            int index = ConvertHelper.ToInt32NoNull(frame.Pop());
            Array array = (Array)frame.Pop()!;
            array.SetValue(value, index);
            return 1;
        }
    }
 
    internal sealed class ArrayLengthInstruction : Instruction
    {
        public static readonly ArrayLengthInstruction Instance = new ArrayLengthInstruction();
 
        public override int ConsumedStack => 1;
        public override int ProducedStack => 1;
        public override string InstructionName => "ArrayLength";
 
        private ArrayLengthInstruction() { }
 
        public override int Run(InterpretedFrame frame)
        {
            object obj = frame.Pop()!;
            frame.Push(((Array)obj).Length);
            return 1;
        }
    }
 
    internal static class ConvertHelper
    {
        public static int ToInt32NoNull(object? val)
        {
            // If the value is null, unbox and cast to throw an InvalidOperationException
            // that .NET Framework throws.
            return (val == null) ? (int)(int?)val! : Convert.ToInt32(val);
        }
    }
}