public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
        {
            var type = (Type)instance;

            serialized = new fsData(RuntimeCodebase.SerializeType(type));
            return(fsResult.Success);
        }
        public Type ResolveExpandedType()
        {
            if (RuntimeCodebase.TryDeserializeType(GetExpandedTypeName(), out var type))
            {
                return(type);
            }

            return(null);
        }
        public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
        {
            if (data.IsString == false)
            {
                return(fsResult.Fail("Type converter requires a string"));
            }

            if (RuntimeCodebase.TryDeserializeType(data.AsString, out var type))
            {
                instance = type;
            }
            else
            {
                return(fsResult.Fail($"Unable to find type: '{data.AsString ?? "(null)"}'."));
            }

            return(fsResult.Success);
        }
Beispiel #4
0
        private fsResult InternalDeserialize_3_Inheritance(Type overrideConverterType, fsData data, Type storageType, ref object result, out List <fsObjectProcessor> processors)
        {
            var deserializeResult = fsResult.Success;

            var objectType = storageType;

            // If the serialized state contains type information, then we need to
            // make sure to update our objectType and data to the proper values
            // so that when we construct an object instance later and run
            // deserialization we run it on the proper type.
            if (IsTypeSpecified(data))
            {
                var typeNameData = data.AsDictionary[Key_InstanceType];

                // we wrap everything in a do while false loop so we can break
                // out it
                do
                {
                    if (typeNameData.IsString == false)
                    {
                        deserializeResult.AddMessage(Key_InstanceType + " value must be a string (in " + data + ")");
                        break;
                    }

                    var typeName = typeNameData.AsString;

                    if (!RuntimeCodebase.TryDeserializeType(typeName, out var type))
                    {
                        deserializeResult += fsResult.Fail("Unable to find type: '" + typeName + "'");
                        break;
                    }

                    if (storageType.IsAssignableFrom(type) == false)
                    {
                        deserializeResult.AddMessage("Ignoring type specifier; a field/property of type " + storageType + " cannot hold an instance of " + type);
                        break;
                    }

                    objectType = type;
                }while (false);
            }
            RemapAbstractStorageTypeToDefaultType(ref objectType);

            // We wait until here to actually Invoke_OnBeforeDeserialize because
            // we do not have the correct set of processors to invoke until
            // *after* we have resolved the proper type to use for
            // deserialization.
            processors = GetProcessors(objectType);

            if (deserializeResult.Failed)
            {
                return(deserializeResult);
            }

            // LAZLO / LUDIQ FIX
            try
            {
                Invoke_OnBeforeDeserialize(processors, storageType, ref data);
            }
            catch (Exception ex)
            {
                return(deserializeResult += fsResult.Fail(ex.ToString()));
            }

            // Construct an object instance if we don't have one already. We also
            // need to construct an instance if the result type is of the wrong
            // type, which may be the case when we have a versioned import graph.
            if (ReferenceEquals(result, null) || result.GetType() != objectType)
            {
                result = GetConverter(objectType, overrideConverterType).CreateInstance(data, objectType);
            }

            // We call OnBeforeDeserializeAfterInstanceCreation here because we
            // still want to invoke the method even if the user passed in an
            // existing instance.
            try
            {
                Invoke_OnBeforeDeserializeAfterInstanceCreation(processors, storageType, result, ref data);
            }
            catch (Exception ex)
            {
                return(deserializeResult += fsResult.Fail(ex.ToString()));
            }

            // NOTE: It is critically important that we pass the actual
            //       objectType down instead of using result.GetType() because it
            //       is not guaranteed that result.GetType() will equal
            //       objectType, especially because some converters are known to
            //       return dummy values for CreateInstance() (for example, the
            //       default behavior for structs is to just return the type of
            //       the struct).

            return(deserializeResult += InternalDeserialize_4_Cycles(overrideConverterType, data, objectType, ref result));
        }
Beispiel #5
0
        private fsResult InternalSerialize_2_Inheritance(Type storageType, Type overrideConverterType, object instance, out fsData data)
        {
            // Serialize the actual object with the field type being the same as
            // the object type so that we won't go into an infinite loop.
            var serializeResult = InternalSerialize_3_ProcessVersioning(overrideConverterType, instance, out data);

            if (serializeResult.Failed)
            {
                return(serializeResult);
            }

            // Do we need to add type information? If the field type and the
            // instance type are different then we will not be able to recover
            // the correct instance type from the field type when we deserialize
            // the object.
            //
            // Note: We allow converters to request that we do *not* add type
            //       information.
            if (storageType != instance.GetType() &&
                GetConverter(storageType, overrideConverterType).RequestInheritanceSupport(storageType))
            {
                var instanceType = instance.GetType();

                // LAZLO / LUDIQ
                // We need to loosen the instance type hint of Unity objects when serializing
                // to counter a very specific issue that happens when:
                //  - We serialize a reference to an object of an editor type
                //  - That object type inherits a runtime type
                //  - The field supports the runtime type, but will allow the editor type
                //  - Therefore serializing as the editor type is valid, but will fail to deserialize in builds
                // The only current example of this bug is AudioMixers and AudioMixerGroups.
                // UnityEDITOR.Audio.AudioMixerController extends UnityENGINE.Audio.AudioMixer.
                // UnityEDITOR.Audio.AudioMixerGroupController extends UnityENGINE.Audio.AudioMixerGroup.
                // Therefore, if we serialize a type hint to the editor controller, e.g. AudioMixerGroupController,
                // builds will fail to deserialize the type hint, even though they don't actually need it
                // to properly fetch the Unity Object reference, because it's provided directly by the converter.
                // We must instead serialize a type hint to the runtime, non-controller type, e.g. AudioMixer.
                // However, when loosening our type, we must make sure not to go past the compatibility
                // with the defined storage type, because if we did, we would get the "Ignoring type specifier"
                // error defined below, as the instance type hint wouldn't be assignable to the storage type on deserialization.
                // Likewise, we must make sure not to go above UnityObject itself, because we need that much hinting
                // for FullSerializer to know that the proper converter to be used is our custom UnityObjectConverter.
                // See: https://support.ludiq.io/communities/5/topics/1032-audio-mixer-reference-gets-nulled-on-il2cpp-builds
                if (instance is UnityObject)
                {
                    var looseType = instanceType;

                    do
                    {
                        instanceType = looseType;
                        looseType    = looseType.BaseType;
                    }while (looseType != null && instanceType != typeof(UnityObject) && storageType.IsAssignableFrom(looseType));

                    // Debug.Log($"Loosened instance type hint for {instance.GetType()} stored as {storageType} to {instanceType}");
                }

                // Add the inheritance metadata
                EnsureDictionary(data);
                data.AsDictionary[Key_InstanceType] = new fsData(RuntimeCodebase.SerializeType(instanceType));
            }

            return(serializeResult);
        }