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

[BUG] Add try-catch to ReflectionTools to suppress ReflectionTypeLoadException #21

Open
laicasaane opened this issue Oct 11, 2021 · 3 comments · May be fixed by #22
Open

[BUG] Add try-catch to ReflectionTools to suppress ReflectionTypeLoadException #21

laicasaane opened this issue Oct 11, 2021 · 3 comments · May be fixed by #22
Assignees
Labels
bug Something isn't working

Comments

@laicasaane
Copy link

laicasaane commented Oct 11, 2021

Describe the bug
Encounter this error when click on a GenesisSettings asset

ReflectionTypeLoadException: Exception of type 'System.Reflection.ReflectionTypeLoadException' was thrown.
System.Reflection.Assembly.GetTypes () (at <695d1cc93cca45069c528c15c9fdd749>:0)
Genesis.Shared.ReflectionTools.GetAllImplementingInstancesOfInterface[T] () (at /home/runner/work/Genesis/Genesis/Genesis/ExternalApp/Genesis.Shared/Tools/ReflectionTools.cs:319)
JCMG.Genesis.Editor.Inspectors.GenesisSettingsInspector..cctor () (at Library/PackageCache/[email protected]/Scripts/Editor/Inspectors/GenesisSettingsInspector.cs:43)
Rethrow as TypeInitializationException: The type initializer for 'JCMG.Genesis.Editor.Inspectors.GenesisSettingsInspector' threw an exception.
UnityEditor.UIElements.InspectorElement+<>c__DisplayClass59_0.<CreateIMGUIInspectorFromEditor>b__0 () (at <1ada6c7052bb42378c5ec1bd01fc4723>:0)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)

Unity Version:
Unity 2020.3.20f1
Genesis is installed via OpenUPM

To Reproduce
This is currently a private project so I cannot share steps to reproduce, but I can describe a bit:

  1. I created a plugin to generate some "union" structs defined by this attribute
    [Union(typeof((<...tuple syntax...>)))]
  2. The user codes in Unity are placed inside a folder with an asmdef (Game.Runtime.asmdef)
    For example:
namespace Game.Runtime
{
    public partial class TestBehaviour : MonoBehaviour
    {
        public partial class Container
        {
            [Union(typeof((Vector2, MySpecialStruct)))]
            public readonly partial struct ExampleUnion
            {
            }
        }
    }

    public struct MySpecialStruct
    {
        public event Action OnClick;

        public float X { get; }

        public int Y;
    }
}
  1. Then press "Generate" button on the GenesisSettings and we will have this code:
namespace Game.Runtime
{
    partial class TestBehaviour {
        partial class Container {

            [StructLayout(LayoutKind.Explicit, Pack = 1)]
            partial struct ExampleUnion
            {
                [FieldOffset(0)]
                private readonly UnityEngine.Vector2 _Item1;

                [FieldOffset(0)]
                private readonly Game.Runtime.MySpecialStruct _Item2;
             }
        }
    }
}
  1. After step 3, whenever I clicked on a GenesisSettings asset, it would throw ReflectionTypeLoadException.
  2. If I replace MySpecialStruct in the Union attribute with float or Vector3, no exception will occur.

Expected behavior
ReflectionTypeLoadException should be suppress in ReflectionTools.cs

@laicasaane laicasaane added the bug Something isn't working label Oct 11, 2021
@jeffcampbellmakesgames
Copy link
Owner

Hi @laicasaane , I just wanted to give you a heads up that I did see this and will be following up this weekend to go through this and the associated fix.

@jeffcampbellmakesgames
Copy link
Owner

jeffcampbellmakesgames commented Apr 18, 2022

@laicasaane What is the definition of the Union attribute in this case? Attempting to reproduce this locally has been a bit difficult without knowing what the constructor param typeof((Vector2, MySpecialStruct) resolves to.

@laicasaane
Copy link
Author

laicasaane commented Apr 19, 2022

@jeffcampbellmakesgames This is the usage of Union attribute

Usage:

using UnityEngine;
using ZBase.Common.Unions;

namespace Examples
{
    public partial class ExampleBehaviour : MonoBehaviour
    {
        [Union(typeof((int, float)))]
        public readonly partial struct ExampleUnionA { }
    }
}

Generated code:

//------------------------------------------------------------------------------
// <auto-generated>
//		This code was generated by a tool (Genesis v2.3.2.0).
//
//
//		Changes to this file may cause incorrect behavior and will be lost if
//		the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Runtime.InteropServices;
using ZBase.Common.Unions;

namespace Examples
{
partial class ExampleBehaviour {

    [StructLayout(LayoutKind.Explicit, Pack = 1)]
    partial struct ExampleUnionA : IUnion, IEquatable<ExampleUnionA>
    {
        public enum Fields : byte
        {
            Item1,
            Item2,
        }

        [FieldOffset(0)]
        public readonly Fields Field;

        [FieldOffset(1)]
        public readonly int Item1;

        [FieldOffset(1)]
        public readonly float Item2;

        public ExampleUnionA(int value)
        {
            Field = Fields.Item1;

            Item2 = default;

            Item1 = value;
        }

        public ExampleUnionA(in int value)
        {
            Field = Fields.Item1;

            Item2 = default;

            Item1 = value;
        }

        public ExampleUnionA(float value)
        {
            Field = Fields.Item2;

            Item1 = default;

            Item2 = value;
        }

        public ExampleUnionA(in float value)
        {
            Field = Fields.Item2;

            Item1 = default;

            Item2 = value;
        }

        public ExampleUnionA(Fields type)
        {
            Field = type;

            Item1 = default;
            Item2 = default;
        }

        public bool TryGet(out int value)
        {
            if (Field != Fields.Item1)
            {
                value = default;
                return false;
            }

            value = Item1;
            return true;
        }

        public bool TryGet(out float value)
        {
            if (Field != Fields.Item2)
            {
                value = default;
                return false;
            }

            value = Item2;
            return true;
        }

        public System.Type GetUnderlyingType()
        {
            if (Field == Fields.Item1)
                return Item1.GetType();

            if (Field == Fields.Item2)
                return Item2.GetType();

            return GetType();
        }

        public override int GetHashCode()
        {
            var hash = new HashCode();
            hash.Add(Field);

            if (Field == Fields.Item1)
                hash.Add(Item1);

            if (Field == Fields.Item2)
                hash.Add(Item2);

            return hash.ToHashCode();
        }

        public override bool Equals(object obj)
            => obj is ExampleUnionA other && Equals(this, other);

        public bool Equals(ExampleUnionA other)
            => Equals(this, other);

        public static bool Equals(ExampleUnionA a, ExampleUnionA b)
        {
            if (a.Field != b.Field)
                return false;

            if (a.Field == Fields.Item1)
                return a.Item1 == b.Item1;

            if (a.Field == Fields.Item2)
                return a.Item2 == b.Item2;

            return false;
        }

        public static bool operator ==(ExampleUnionA left, ExampleUnionA right)
            => Equals(left, right);

        public static bool operator !=(ExampleUnionA left, ExampleUnionA right)
            => !Equals(left, right);

        public bool Equals(in ExampleUnionA other)
            => Equals(in this, in other);

        public static bool Equals(in ExampleUnionA a, in ExampleUnionA b)
        {
            if (a.Field != b.Field)
                return false;

            if (a.Field == Fields.Item1)
                return a.Item1 == b.Item1;

            if (a.Field == Fields.Item2)
                return a.Item2 == b.Item2;

            return false;
        }

        public static bool operator ==(in ExampleUnionA left, in ExampleUnionA right)
            => Equals(in left, in right);

        public static bool operator !=(in ExampleUnionA left, in ExampleUnionA right)
            => !Equals(in left, in right);

        public override string ToString()
        {
            if (Field == Fields.Item1)
                return Item1.ToString();

            if (Field == Fields.Item2)
                return Item2.ToString();

            return string.Empty;
        }

        public static implicit operator ExampleUnionA(int value)
            => new ExampleUnionA(value);

        public static implicit operator int(ExampleUnionA value)
            => value.Item1;

        public static implicit operator ExampleUnionA(float value)
            => new ExampleUnionA(value);

        public static implicit operator float(ExampleUnionA value)
            => value.Item2;
    }
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants