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