public static void AssignGcHandle(IntPtr pointer, GCHandle gcHandle)
        {
            var handleAsPointer = GCHandle.ToIntPtr(gcHandle);

            if (pointer == IntPtr.Zero)
            {
                throw new NullReferenceException(nameof(pointer));
            }
            var objectKlass           = (Il2CppClass *)IL2CPP.il2cpp_object_get_class(pointer);
            var targetGcHandlePointer = IntPtr.Add(pointer, (int)UnityVersionHandler.Wrap(objectKlass).InstanceSize - IntPtr.Size);

            *(IntPtr *)targetGcHandlePointer = handleAsPointer;
        }
        public static void RegisterTypeInIl2Cpp <T>() where T : class
        {
            var type = typeof(T);

            if (type.IsGenericType || type.IsGenericTypeDefinition)
            {
                throw new ArgumentException($"Type {type} is generic and can't be used in il2cpp");
            }

            var currentPointer = Il2CppClassPointerStore <T> .NativeClassPtr;

            if (currentPointer != IntPtr.Zero)
            {
                throw new ArgumentException($"Type {type} is already registered in il2cpp");
            }

            var baseType         = type.BaseType;
            var baseClassPointer = UnityVersionHandler.Wrap((Il2CppClass *)ReadClassPointerForType(baseType));

            if (baseClassPointer == null)
            {
                throw new ArgumentException($"Base class {baseType} of class {type} is not registered in il2cpp");
            }

            if ((baseClassPointer.Bitfield1 & ClassBitfield1.valuetype) != 0 || (baseClassPointer.Bitfield1 & ClassBitfield1.enumtype) != 0)
            {
                throw new ArgumentException($"Base class {baseType} is value type and can't be inherited from");
            }

            if ((baseClassPointer.Bitfield1 & ClassBitfield1.is_generic) != 0)
            {
                throw new ArgumentException($"Base class {baseType} is generic and can't be inherited from");
            }

            if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_SEALED) != 0)
            {
                throw new ArgumentException($"Base class {baseType} is sealed and can't be inherited from");
            }

            if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_INTERFACE) != 0)
            {
                throw new ArgumentException($"Base class {baseType} is an interface and can't be inherited from");
            }

            lock (InjectedTypes)
                if (!InjectedTypes.Add(typeof(T).FullName))
                {
                    throw new ArgumentException($"Type with FullName {typeof(T).FullName} is already injected. Don't inject the same type twice, or use a different namespace");
                }

            if (ourOriginalTypeToClassMethod == null)
            {
                HookClassFromType();
            }

            var classPointer = UnityVersionHandler.NewClass(baseClassPointer.VtableCount);

            classPointer.Image        = FakeImage;
            classPointer.Parent       = baseClassPointer.ClassPointer;
            classPointer.ElementClass = classPointer.Class = classPointer.CastClass = classPointer.ClassPointer;
            classPointer.NativeSize   = -1;
            classPointer.ActualSize   = classPointer.InstanceSize = baseClassPointer.InstanceSize + (uint)IntPtr.Size;
            classPointer.Bitfield1    = ClassBitfield1.initialized | ClassBitfield1.initialized_and_no_error |
                                        ClassBitfield1.size_inited;
            classPointer.Bitfield2 = ClassBitfield2.has_finalize | ClassBitfield2.is_vtable_initialized;
            classPointer.Name      = Marshal.StringToHGlobalAnsi(type.Name);
            classPointer.Namespace = Marshal.StringToHGlobalAnsi(type.Namespace);

            classPointer.ThisArg.type           = classPointer.ByValArg.type = Il2CppTypeEnum.IL2CPP_TYPE_CLASS;
            classPointer.ThisArg.mods_byref_pin = 64;

            classPointer.Flags = baseClassPointer.Flags; // todo: adjust flags?

            var eligibleMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Where(IsMethodEligible).ToArray();
            var methodCount     = 2 + eligibleMethods.Length; // 1 is the finalizer, 1 is empty ctor

            classPointer.MethodCount = (ushort)methodCount;
            var methodPointerArray = (Il2CppMethodInfo **)Marshal.AllocHGlobal(methodCount * IntPtr.Size);

            classPointer.Methods = methodPointerArray;

            methodPointerArray[0] = ConvertStaticMethod(FinalizeDelegate, "Finalize", classPointer);
            methodPointerArray[1] = ConvertStaticMethod(CreateEmptyCtor(type), ".ctor", classPointer);
            for (var i = 0; i < eligibleMethods.Length; i++)
            {
                var methodInfo = eligibleMethods[i];
                methodPointerArray[i + 2] = ConvertMethodInfo(methodInfo, classPointer);
            }

            var vTablePointer     = (VirtualInvokeData *)classPointer.VTable;
            var baseVTablePointer = (VirtualInvokeData *)baseClassPointer.VTable;

            classPointer.VtableCount = baseClassPointer.VtableCount;
            for (var i = 0; i < classPointer.VtableCount; i++)
            {
                vTablePointer[i] = baseVTablePointer[i];
                if (Marshal.PtrToStringAnsi(vTablePointer[i].method->name) == "Finalize") // slot number is not static
                {
                    vTablePointer[i].method    = methodPointerArray[0];
                    vTablePointer[i].methodPtr = methodPointerArray[0]->methodPointer;
                }
            }

            var newCounter = Interlocked.Decrement(ref ourClassOverrideCounter);

            FakeTokenClasses[newCounter] = classPointer.Pointer;
            classPointer.ByValArg.data   = classPointer.ThisArg.data = (IntPtr)newCounter;

            RuntimeSpecificsStore.SetClassInfo(classPointer.Pointer, true, true);
            Il2CppClassPointerStore <T> .NativeClassPtr = classPointer.Pointer;

            LogSupport.Info($"Registered mono type {typeof(T)} in il2cpp domain");
        }