diff --git a/nanoFramework.CoreLibrary/CoreLibrary.nfproj b/nanoFramework.CoreLibrary/CoreLibrary.nfproj index e1501725..0bd250ab 100644 --- a/nanoFramework.CoreLibrary/CoreLibrary.nfproj +++ b/nanoFramework.CoreLibrary/CoreLibrary.nfproj @@ -74,6 +74,7 @@ + @@ -262,4 +263,4 @@ - + \ No newline at end of file diff --git a/nanoFramework.CoreLibrary/System/Array.cs b/nanoFramework.CoreLibrary/System/Array.cs index 86d93f12..c9cf7254 100644 --- a/nanoFramework.CoreLibrary/System/Array.cs +++ b/nanoFramework.CoreLibrary/System/Array.cs @@ -3,6 +3,10 @@ using System.Collections; using System.Runtime.CompilerServices; +#if NANOCLR_REFLECTION +using System.Collections.Generic; +using System.Diagnostics; +#endif // NANOCLR_REFLECTION namespace System { @@ -429,5 +433,153 @@ public void Reset() _index = _startIndex - 1; } } + +#if NANOCLR_REFLECTION +#pragma warning disable CA1822 // Mark members as static + //---------------------------------------------------------------------------------------- + // ! READ THIS BEFORE YOU WORK ON THIS CLASS. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not SZArrayHelper objects. Rather, they are of type U[] + // where U[] is castable to T[]. No actual SZArrayHelper object is ever instantiated. Thus, you will + // see a lot of expressions that cast "this" "T[]". + // + // This class is needed to allow an SZ array of type T[] to expose IList, + // IList, etc., etc. all the way up to IList. When the following call is + // made: + // + // ((IList) (new U[n])).SomeIListMethod() + // + // the interface stub dispatcher treats this as a special case, loads up SZArrayHelper, + // finds the corresponding generic method (matched simply by method name), instantiates + // it for type and executes it. + // + // The "T" will reflect the interface used to invoke the method. The actual runtime "this" will be + // array that is castable to "T[]" (i.e. for primitives and valuetypes, it will be exactly + // "T[]" - for orefs, it may be a "U[]" where U derives from T.) + //---------------------------------------------------------------------------------------- + internal sealed class SZArrayHelper + { + // It is never legal to instantiate this class. + private SZArrayHelper() + { + Debug.Assert(false, "Hey! How'd I get here?"); + } + + internal IEnumerator GetEnumerator() + { + // ! Warning: "this" is an array, not an SZArrayHelper. See comments above + // ! or you may introduce a security hole! + T[] @this = Unsafe.As(this); + int length = @this.Length; + return length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(@this, length); + } + + private void CopyTo(T[] array, int index) + { + // ! Warning: "this" is an array, not an SZArrayHelper. See comments above + // ! or you may introduce a security hole! + + T[] @this = Unsafe.As(this); + Array.Copy(@this, 0, array, index, @this.Length); + } + + internal int get_Count() + { + // ! Warning: "this" is an array, not an SZArrayHelper. See comments above + // ! or you may introduce a security hole! + T[] @this = Unsafe.As(this); + return @this.Length; + } + + internal T get_Item(int index) + { + // ! Warning: "this" is an array, not an SZArrayHelper. See comments above + // ! or you may introduce a security hole! + + T[] @this = Unsafe.As(this); + if ((uint)index >= (uint)@this.Length) + { +#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one + throw new ArgumentOutOfRangeException(); +#pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one + } + + return @this[index]; + } + + internal void set_Item(int index, T value) + { + // ! Warning: "this" is an array, not an SZArrayHelper. See comments above + // ! or you may introduce a security hole! + T[] @this = Unsafe.As(this); + if ((uint)index >= (uint)@this.Length) + { +#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one + throw new ArgumentOutOfRangeException(); +#pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one + } + + @this[index] = value; + } + + private void Add(T _) + { + // Not meaningful for arrays. + throw new NotSupportedException(); + } + + private bool Contains(T value) + { + // ! Warning: "this" is an array, not an SZArrayHelper. See comments above + // ! or you may introduce a security hole! + T[] @this = Unsafe.As(this); + return Array.IndexOf(@this, value, 0, @this.Length) >= 0; + } + + private bool get_IsReadOnly() + { + return true; + } + + private void Clear() + { + // ! Warning: "this" is an array, not an SZArrayHelper. See comments above + // ! or you may introduce a security hole! + + throw new NotSupportedException(); + } + + private int IndexOf(T value) + { + // ! Warning: "this" is an array, not an SZArrayHelper. See comments above + // ! or you may introduce a security hole! + T[] @this = Unsafe.As(this); + return Array.IndexOf(@this, value, 0, @this.Length); + } + + private void Insert(int _, T _1) + { + // Not meaningful for arrays + throw new NotSupportedException(); + } + + private bool Remove(T _) + { + // Not meaningful for arrays + throw new NotSupportedException(); + return default; + } + + private void RemoveAt(int _) + { + // Not meaningful for arrays + throw new NotSupportedException(); + } + } +#pragma warning restore CA1822 + +#endif } } diff --git a/nanoFramework.CoreLibrary/System/Runtime/CompilerServices/Unsafe.cs b/nanoFramework.CoreLibrary/System/Runtime/CompilerServices/Unsafe.cs new file mode 100644 index 00000000..6d784b10 --- /dev/null +++ b/nanoFramework.CoreLibrary/System/Runtime/CompilerServices/Unsafe.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if NANOCLR_REFLECTION +#nullable enable + +namespace System.Runtime.CompilerServices +{ + /// + /// Contains generic, low-level functionality for manipulating pointers. + /// + public static unsafe partial class Unsafe + { + /// + /// Casts the given object to the specified type, performs no dynamic type checking. + /// + /// The target reference type. The return value will be of this type. + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern T? As(object? o) where T : class?; + } +} + +#endif