Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static fields and static constructors aren't initialized/called when assembly is loaded in code #733

Closed
MateuszKlatecki opened this issue Apr 20, 2021 · 1 comment · Fixed by nanoframework/nf-interpreter#1973

Comments

@MateuszKlatecki
Copy link
Member

Details about Problem

Target: any

Firmware image version: newest

Worked before? If so, with which nanoFramework image version: I checked the stable version and it also doesn't work

Device capabilities output:

Description

I have created a library that I am going to manually load during runtime with Assemby.Load. In the library I have classes that have static fields and static constructors. Fields are not automatically initialized and constructors are not called when assembly is loaded or before first use.

Detailed repro steps so we can see the same problem

  1. Create simple library with static fields and static constructor for example something like this
    public class MyClass
    {
        private static int[] storage = new int[8];  //this static field isn't initialized

        // this static constructor isn't called automatically
        static MyClass()
        {
            storage = new int[16];
        }
        public static void Store(int position, int value)
        {
            if (storage == null || position < 0 || position >= storage.Length)
                return;

            storage[position] = value;
        }

        public static int Load(int position)
        {
            if (storage == null || position < 0 || position >= storage.Length)
                return int.MinValue;
            return storage[position];
        }
    }
  1. Wrie program that load assembly and try to run methods:
using System.Device.Gpio;
using System.Diagnostics;
using System.Reflection;
using System.Threading;

namespace Program
{
    public class Program
    {
        private static GpioController s_GpioController;


        public static void Main()
        {
            s_GpioController = new GpioController();
            GpioPin led = s_GpioController.OpenPin(13, PinMode.Output);
            led.Write(PinValue.High);

            Debug.WriteLine("Hello from nanoFramework!");


            // array below is a pe file from this code:
            //public class MyClass
            //{
            //    private static int[] storage = new int[8];  //this static field isn't initialized

            //    // this static constructor isn't called automatically
            //    static MyClass()
            //    {
            //        storage = new int[16];
            //    }
            //    public static void Store(int position, int value)
            //    {
            //        if (storage == null || position < 0 || position >= storage.Length)
            //            return;

            //        storage[position] = value;
            //    }

            //    public static int Load(int position)
            //    {
            //        if (storage == null || position < 0 || position >= storage.Length)
            //            return int.MinValue;
            //        return storage[position];
            //    }
            //}

            byte[] myLib_pe = {
                0x4E, 0x46, 0x4D, 0x52, 0x4B, 0x31, 0x00, 0x00, 0x6C, 0x77, 0x97, 0xD8,
                0xA5, 0x2E, 0xBA, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x01, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
                0x98, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00,
                0xB8, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
                0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
                0x00, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00,
                0xA0, 0x01, 0x00, 0x00, 0xA0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x01, 0x02, 0x00, 0x00, 0xD2, 0xFC, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00,
                0x03, 0x00, 0x00, 0x00, 0xB1, 0xFE, 0x40, 0xFE, 0x00, 0x00, 0x00, 0x00,
                0xF7, 0xFE, 0x40, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF,
                0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x03, 0x11, 0x00, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x01, 0x00, 0x15, 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x11, 0x00,
                0xFE, 0xFF, 0x00, 0x00, 0x86, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
                0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0x91, 0x24, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x06, 0x00, 0x0F, 0x00, 0x17, 0x00,
                0x96, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x03, 0x0E, 0x00, 0x09, 0x00,
                0xDD, 0xFE, 0x3C, 0x00, 0x96, 0x00, 0x00, 0x00, 0x07, 0x01, 0x02, 0x02,
                0x10, 0x00, 0x0F, 0x00, 0x00, 0x4D, 0x79, 0x4C, 0x69, 0x62, 0x00, 0x4D,
                0x79, 0x43, 0x6C, 0x61, 0x73, 0x73, 0x00, 0x53, 0x74, 0x6F, 0x72, 0x65,
                0x00, 0x73, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00,
                0x20, 0x00, 0x00, 0x06, 0x13, 0x07, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
                0x07, 0x07, 0x01, 0x00, 0x01, 0x07, 0x07, 0x00, 0x02, 0x28, 0x00, 0x80,
                0x00, 0x2A, 0x1E, 0x8D, 0x01, 0x40, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x10,
                0x8D, 0x01, 0x40, 0x80, 0x00, 0x00, 0x2A, 0x00, 0x7E, 0x00, 0x00, 0x2C,
                0x11, 0x02, 0x16, 0x32, 0x0D, 0x02, 0x7E, 0x00, 0x00, 0x8E, 0x69, 0xFE,
                0x04, 0x16, 0xFE, 0x01, 0x2B, 0x01, 0x17, 0x0A, 0x06, 0x2C, 0x02, 0x2B,
                0x06, 0x7E, 0x00, 0x00, 0x02, 0x03, 0x9E, 0x2A, 0x00, 0x7E, 0x00, 0x00,
                0x2C, 0x11, 0x02, 0x16, 0x32, 0x0D, 0x02, 0x7E, 0x00, 0x00, 0x8E, 0x69,
                0xFE, 0x04, 0x16, 0xFE, 0x01, 0x2B, 0x01, 0x17, 0x0A, 0x06, 0x2C, 0x08,
                0x20, 0x00, 0x00, 0x00, 0x80, 0x0B, 0x2B, 0x08, 0x7E, 0x00, 0x00, 0x02,
                0x94, 0x0B, 0x2B, 0x00, 0x07, 0x2A, 0x00, 0x00
            };


            var loadedAssembly = Assembly.Load(myLib_pe);

            if (loadedAssembly != null)
            {

                var type = loadedAssembly.GetTypes()[0];

                var storeMethod = type.GetMethod("Store");
                var loadMethod = type.GetMethod("Load");

                if (storeMethod != null)
                {
                    //store 5 at position 2
                    storeMethod.Invoke(null, new object[] { 2, 5 });

                    //load value form second position
                    int value = (int)loadMethod.Invoke(null, new object[] { 2 });

                    //if value is int.MinValue then infinite fast blinking
                    if (value == int.MinValue)
                    {
                        for (; ; )
                        {
                            led.Write(PinValue.Low);
                            Thread.Sleep(25);
                            led.Write(PinValue.High);
                            Thread.Sleep(25);
                        }
                    }

                    // if value is positive blink correct number times
                    while (value-- > 0)
                    {
                        led.Write(PinValue.Low);
                        Thread.Sleep(250);
                        led.Write(PinValue.High);
                        Thread.Sleep(250);
                    }
                }
            }

            Thread.Sleep(Timeout.Infinite);
        }
    }
}

...

Expected behaviour

Static constructor should be called and field initialized before first usage.

Additional context

If library is referenced (not loaded during runtime) then everything seems to work correctly.

@Korporal
Copy link

Korporal commented May 6, 2021

@MateuszKlatecki - I seem to recall discovering a bug in the way static storage was initialized, this was a couple of years ago now and I think a code change was made then merged by @josesimoes but as I say it was a couple of years ago.

@josesimoes josesimoes changed the title Static fields isn't initialized and static constructors isn't called when assembly is loaded manually Static fields and static constructors aren't initialized/called when assembly is loaded in code Jul 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants