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

UnityEngine.Vector3 serializes but does not deserialize (with fix!) #130

Open
jhughes2112 opened this issue Jul 20, 2021 · 2 comments
Open

Comments

@jhughes2112
Copy link

jhughes2112 commented Jul 20, 2021

Here's the error (which was reported previously, but nobody actually solved):

InvalidProgramException: Invalid IL code in (wrapper dynamic-method) FakeVector3:_cgm (object): IL_0013: ret       

System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, System.Boolean throwOnBindFailure, System.Boolean allowClosed) (at <695d1cc93cca45069c528c15c9fdd749>:0)

Here is some simple test code that shows it broken:

public struct FakeVector3
{
	public float x;
	public float y;
	public float z;

	public static FakeVector3 right { get { return new FakeVector3() { x=1,y=2,z=3 }; } }
}

FakeVector3 v = new FakeVector3() { x=0, y=1, z=2 };
string json = fastJSON.JSON.ToJSON(v);
FakeVector3 newV = fastJSON.JSON.ToObject<FakeVector3>(json);

Basically what is happening is the Reflection.CreateGetMethod() was doing the wrong thing for static getters in structs. Here's how I modified it to work, which does deserialize properly:

        internal static GenericGetter CreateGetMethod(Type type, PropertyInfo propertyInfo)
        {
            MethodInfo getMethod = propertyInfo.GetGetMethod();
            if (getMethod == null)
                return null;

            DynamicMethod getter = new DynamicMethod("_cgm", typeof(object), new Type[] { typeof(object) }, type, true);

            ILGenerator il = getter.GetILGenerator();

            if (!type.IsClass) // structs
            {
                var lv = il.DeclareLocal(type);
		if (!getMethod.IsStatic)
		{
			il.Emit(OpCodes.Ldarg_0);
			il.Emit(OpCodes.Unbox_Any, type);
			il.Emit(OpCodes.Stloc_0);
			il.Emit(OpCodes.Ldloca_S, lv);
			il.EmitCall(OpCodes.Call, getMethod, null);
		}
		else  // call a static method on a struct type
		{
			il.Emit(OpCodes.Call, getMethod);
		}
		if (propertyInfo.PropertyType.IsValueType)
			il.Emit(OpCodes.Box, propertyInfo.PropertyType);
            }
            else
            {
                if (!getMethod.IsStatic)
                {
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
                    il.EmitCall(OpCodes.Callvirt, getMethod, null);
                }
                else
                    il.Emit(OpCodes.Call, getMethod);

                if (propertyInfo.PropertyType.IsValueType)
                    il.Emit(OpCodes.Box, propertyInfo.PropertyType);
            }

            il.Emit(OpCodes.Ret);

            return (GenericGetter)getter.CreateDelegate(typeof(GenericGetter));
        }
@jhughes2112
Copy link
Author

Since I'm giving you code, here's an updated version of WriteString() that is table driven and significantly faster than what's there. I was profiling today and this was coming up a lot for long documents. It removes the vast majority of the comparisons using a simple lookup table.

In my version, I also removed all the ToLowerInvariant() support, because that creates a lot of allocations whether you enable the write-as-lower feature or not.

Enjoy!

        static private bool[] escapedUnicodeSet = null;
        static private bool[] normalSet = null;
        private void WriteString(string s)
        {
            // Build lookup tables
            if (escapedUnicodeSet==null)
            {
                escapedUnicodeSet = new bool[256];
                normalSet = new bool[256];
                for (int c=0; c<256; c++)
                {
                    escapedUnicodeSet[c] = (c >= ' ' && c < 128 && c != '\"' && c != '\\');
                    normalSet[c] = (c != '\t' && c != '\n' && c != '\r' && c != '\"' && c != '\\' && c != '\0'); // && c != ':' && c!=',')
                }
            }

            _output.Append('\"');

            bool[] setToUse = _useEscapedUnicode ? escapedUnicodeSet : normalSet;  // table-driven removes almost all the comparisons
            int runIndex = -1;
            int l = s.Length;
            for (var index = 0; index < l; ++index)
            {
                var c = s[index];

                if (setToUse[c])
                {
                    if (runIndex == -1)
                        runIndex = index;
                    continue;
                }

                if (runIndex != -1)
                {
                    _output.Append(s, runIndex, index - runIndex);
                    runIndex = -1;
                }

                switch (c)
                {
                    case '\t': _output.Append('\\').Append('t'); break;
                    case '\r': _output.Append('\\').Append('r'); break;
                    case '\n': _output.Append('\\').Append('n'); break;
                    case '"':
                    case '\\': _output.Append('\\'); _output.Append(c); break;
                    case '\0': _output.Append("\\u0000"); break;
                    default:
                        if (_useEscapedUnicode)
                        {
                            _output.Append("\\u");
                            _output.Append(((int)c).ToString("X4", NumberFormatInfo.InvariantInfo));
                        }
                        else
                            _output.Append(c);

                        break;
                }
            }

            if (runIndex != -1)
                _output.Append(s, runIndex, s.Length - runIndex);

            _output.Append('\"');
        }

@mgholam
Copy link
Owner

mgholam commented Jul 23, 2021

Thanks!
I will check and let you know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants