Exemple #1
0
        void EmitSimpleStream(Schema schema, ValueWriter writer)
        {
            // push *value
            Emit.LoadArgument(0);
            LoadIndirect(schema.NetType);

            // push stream
            Emit.LoadArgument(ARG_STREAM);

            // push buffer
            Emit.LoadArgument(ARG_BUFFER);

            // push &available
            LocalAvailable = Emit.DeclareLocal(typeof(int), "available");
            PushTotalAvailable();
            Emit.StoreLocal(LocalAvailable);
            Emit.LoadLocalAddress(LocalAvailable);

            // push &ptr;
            Emit.LoadArgumentAddress(ARG_POINTER);

            // call writer
            Emit.Call(writer.MethodInfo);

            Flush(resetAvailable: false, writtenTop: false);

            Emit.Return();
        }
Exemple #2
0
        void EmitSimpleComplete(Schema schema, ValueWriter writer)
        {
            var effective = schema.NetType;

            Sigil.Label theEnd = null;

            if (Nullable.GetUnderlyingType(effective) != null)
            {
                Emit.Duplicate();                 // prepare argument 0 for GetValueOrDefault call

                Emit.Call(effective.GetProperty("HasValue").GetGetMethod());

                var hasValueTrue = Emit.DefineLabel("HasValue_true");
                Emit.BranchIfTrue(hasValueTrue);
                Emit.Pop();                 // discard argument 0
                // byte* destination
                Emit.LoadArgument(1);
                // available
                if (Destination == DestinationType.Stream)
                {
                    Emit.LoadLocal(LocalAvailable);
                }
                else
                {
                    Emit.LoadArgument(2);
                }
                // .WriteNull
                Emit.Call(WriteNull);

                if (Destination == DestinationType.Stream)
                {
                    Flush();
                }

                theEnd = Emit.DefineLabel("theEnd");
                Emit.Branch(theEnd);

                Emit.MarkLabel(hasValueTrue);

                Emit.Call(effective.GetMethod("GetValueOrDefault", Type.EmptyTypes));

                effective = Nullable.GetUnderlyingType(effective);
            }

            CallWriter(writer, effective, false);

            if (Destination == DestinationType.Stream)
            {
                Flush();
            }

            if (theEnd != null)
            {
                Emit.MarkLabel(theEnd);
            }

            Emit.Return();
        }
Exemple #3
0
        void CallWriter(ValueWriter writer, Type effective, bool useLocals)
        {
            if (writer.MethodInfo == null)
            {
                if (effective == typeof(System.Net.IPAddress))
                {
                    EmitIPAddress(useLocals);
                    return;
                }
                throw new NotImplementedException();
            }

            if (Destination == DestinationType.Pointer || writer.MaxLength.HasValue)
            {
                var firstArg = writer.MethodInfo.GetParameters()[0].ParameterType;
                if (firstArg != effective)
                {
                    Emit.Convert(firstArg);
                }

                if (useLocals)
                {
                    Emit.LoadLocal(LocalDestination);
                    Emit.LoadLocal(LocalAvailable);
                }
                else
                {
                    Emit.LoadArgument(1);
                    if (Destination == DestinationType.Pointer)
                    {
                        Emit.LoadArgument(2);
                    }
                    else
                    {
                        Emit.LoadLocal(LocalAvailable);
                    }
                }
            }
            else
            {
                Emit.LoadArgument(ARG_STREAM);
                Emit.LoadArgument(ARG_BUFFER);
                Emit.LoadLocalAddress(LocalAvailable);
                Emit.LoadLocalAddress(LocalDestination);
            }

            Emit.Call(writer.MethodInfo);
        }
Exemple #4
0
        /// <summary>Must be called with value on top of the stack</summary>
        void EmitInline(Schema schema)
        {
            if (Nullable.GetUnderlyingType(schema.NetType) != null)
            {
                PushAddress(schema.NetType);
            }

            switch (schema.JsonType)
            {
            case JsonType.Array:
                if (schema.NetType.IsArray)
                {
                    EmitArray(schema, pushResult: false);
                }
                else
                {
                    EmitEnumerable(schema, pushResult: false);
                }
                break;

            case JsonType.Object:
                if (schema.Keys != null)
                {
                    EmitDictionary(schema, pushResult: false);
                }
                else
                {
                    EmitObject(schema, pushResult: false);
                }
                break;

            case JsonType.String:
            case JsonType.Number:
            case JsonType.Integer:
            case JsonType.Boolean:
                ValueWriter simpleWriter;
                if (ValueWriter.TryGetWriter(schema.NetType, Destination, out simpleWriter))
                {
                    EmitSimpleInline(schema, simpleWriter);
                    return;
                }
                throw new ArgumentException("JSON primitive writer not found for " + schema.NetType.Name);

            default:
                throw new ArgumentException("Unknown JsonType " + schema.JsonType);
            }
        }
        static ValueWriter From <T>(PrimitiveWriter <T> writer)
        {
            var w = new ValueWriter
            {
                MethodInfo = writer.Method,
                Type       = typeof(T),
            };

            var attr = w.MethodInfo.GetCustomAttribute <ValueWriterAttribute>();

            if (attr != null && attr.MaxLength > 0)
            {
                w.MaxLength = attr.MaxLength;
            }

            return(w);
        }
        internal static bool TryGetWriter(Type type, DestinationType destination, out ValueWriter writer)
        {
            var effective = Nullable.GetUnderlyingType(type) ?? type;

            // TODO support enums as strings too - this won't apply then
            if (effective.IsEnum)
            {
                effective = effective.GetEnumUnderlyingType();
            }

            // check for common (no options writer first)
            var lookup = new Lookup(effective);

            if (_Writers.TryGetValue(lookup, out writer))
            {
                return(true);
            }

            // fallback to using the destination
            lookup = new Lookup(effective, destination);
            return(_Writers.TryGetValue(lookup, out writer));
        }
        static ValueWriter()
        {
            Add <int>(Serializer.WriteInt32, _Writers);
            Add <uint>(Serializer.WriteUInt32, _Writers);
            Add <long>(Serializer.WriteInt64, _Writers);
            Add <ulong>(Serializer.WriteUInt64, _Writers);
            Add <bool>(Serializer.WriteBoolean, _Writers);
            Add <Guid>(Serializer.WriteGuidFormatD, _Writers);
            Add <DateTime>(Serializer.WriteDateTime8601, _Writers);
            Add <float>(Serializer.WriteSingle, _Writers);
            Add <double>(Serializer.WriteDouble, _Writers);
            Add <decimal>(Serializer.WriteDecimal, _Writers);
            Add <char>(ConvertUTF.WriteCharUtf8, _Writers);

            _Writers[typeof(byte)] = new ValueWriter
            {
                MaxLength  = 3,
                Type       = typeof(byte),
                MethodInfo = _Writers[typeof(uint)].MethodInfo,
            };

            _Writers[typeof(sbyte)] = new ValueWriter
            {
                MaxLength  = 4,
                Type       = typeof(sbyte),
                MethodInfo = _Writers[typeof(int)].MethodInfo,
            };

            _Writers[typeof(ushort)] = new ValueWriter
            {
                MaxLength  = 5,
                Type       = typeof(ushort),
                MethodInfo = _Writers[typeof(uint)].MethodInfo,
            };

            _Writers[typeof(short)] = new ValueWriter
            {
                MaxLength  = 6,
                Type       = typeof(short),
                MethodInfo = _Writers[typeof(int)].MethodInfo,
            };

            _Writers[typeof(System.Net.IPAddress)] = new ValueWriter
            {
                MaxLength  = 48,
                Type       = typeof(System.Net.IPAddress),
                MethodInfo = null,                 // IPAddress is a special case to handle v4 and v6 effeciently
            };

            // 2 string options
            Add <string>(ConvertUTF.WriteStringUtf8, _Writers, (long)DestinationType.Pointer);
            _Writers[new Lookup(typeof(string), DestinationType.Stream)] = new ValueWriter
            {
                Type       = typeof(string),
                MethodInfo = typeof(ConvertUTF).GetMethod("WriteToStreamUtf8", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public),
            };

            // 2 byte[] -> base64 options
            Add <byte[]>(Serializer.WriteBase64, _Writers, (long)DestinationType.Pointer);
            _Writers[new Lookup(typeof(byte[]), DestinationType.Stream)] = new ValueWriter
            {
                Type       = typeof(byte[]),
                MethodInfo = typeof(Serializer).GetMethod("WriteBase64ToStream", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public),
            };
        }
 static void Add <T>(PrimitiveWriter <T> writer, Dictionary <Lookup, ValueWriter> dict, long flags = 0)
 {
     _Writers[new Lookup(typeof(T), flags)] = ValueWriter.From(writer);
 }
Exemple #9
0
        static Sigil.NonGeneric.Emit Create <T>(Schema schema, DestinationType destination)
        {
            ValueWriter simpleWriter = null;

            switch (schema.JsonType)
            {
            case JsonType.Array:
            case JsonType.Object:
                // handle these after the switch
                break;

            case JsonType.String:
            case JsonType.Number:
            case JsonType.Integer:
            case JsonType.Boolean:
                if (ValueWriter.TryGetWriter(schema.NetType, destination, out simpleWriter))
                {
                    break;
                }
                throw new ArgumentException("JSON primitive writer not found for " + schema.NetType.Name);

            default:
                throw new ArgumentException("Unknown JsonType " + schema.JsonType);
            }

            var builder = new DelegateBuilder
            {
                Destination = destination,
            };

            Sigil.NonGeneric.Emit emit;
            string methodName = schema.NetType.Name + "_toJSON";

            if (destination == DestinationType.Pointer)
            {
                emit = Sigil.NonGeneric.Emit.NewDynamicMethod(typeof(int), new[] {
                    typeof(T).MakeByRefType(),                          // value
                    typeof(byte).MakePointerType(),                     // destination
                    typeof(int),                                        // available
                }, methodName, doVerify: UseSigilVerify);
            }
            else if (destination == DestinationType.Stream)
            {
                emit = Sigil.NonGeneric.Emit.NewDynamicMethod(typeof(void), new[] {
                    typeof(T).MakeByRefType(),                          // value
                    typeof(byte).MakePointerType(),                     // buffer
                    typeof(System.IO.Stream),                           // destination
                    typeof(byte).MakeArrayType(),                       // buffer
                }, methodName, doVerify: UseSigilVerify);
            }
            else
            {
                throw new ArgumentException();
            }
            builder.Emit = emit;

            if (simpleWriter != null)
            {
                if (destination == DestinationType.Stream)
                {
                    if (!simpleWriter.MaxLength.HasValue)
                    {
                        // special case writers that might have to Flush multiple times
                        builder.EmitSimpleStream(schema, simpleWriter);
                        return(emit);
                    }

                    builder.LocalAvailable = emit.DeclareLocal(typeof(int), "available");

                    builder.PushTotalAvailable();
                    emit.StoreLocal(builder.LocalAvailable);
                }

                emit.LoadArgument(0);
                builder.LoadIndirect(schema.NetType);                 // deref argument 0

                builder.EmitSimpleComplete(schema, simpleWriter);
                return(emit);
            }

            Sigil.Local local;
            // local for dst
            builder.LocalDestination = local = emit.DeclareLocal(typeof(byte *), "destination");
            emit.LoadArgument(1);
            emit.StoreLocal(local);

            // local for avail
            builder.LocalAvailable = local = emit.DeclareLocal(typeof(int), "available");
            builder.PushTotalAvailable();
            emit.StoreLocal(local);

            bool pushResult = destination == DestinationType.Pointer;

            // CRITICAL before calling any composite methods top of stack must be `value` followed by `available` when pushResult is true
            if (pushResult)
            {
                emit.LoadLocal(local);
            }

            emit.LoadArgument(0);
            builder.LoadIndirect(schema.NetType);             // deref argument 0


            if (schema.JsonType == JsonType.Array)
            {
                if (schema.NetType.IsArray)
                {
                    builder.EmitArray(schema, pushResult: pushResult);
                }
                else
                {
                    builder.EmitEnumerable(schema, pushResult: pushResult);
                }
            }
            else if (schema.JsonType == JsonType.Object)
            {
                if (schema.Keys != null)
                {
                    builder.EmitDictionary(schema, pushResult: pushResult);
                }
                else
                {
                    builder.EmitObject(schema, pushResult: pushResult);
                }
            }

            if (destination == DestinationType.Stream)
            {
                builder.Flush(writtenTop: false);
            }
            emit.Return();
            return(emit);
        }
Exemple #10
0
        // never pushes result
        void EmitSimpleInline(Schema schema, ValueWriter writer)
        {
            var effective = schema.NetType;

            Sigil.Label ifNull = null;

            var underlying = Nullable.GetUnderlyingType(effective);

            if (underlying != null)
            {
                ifNull = DefineLabel("ifNull");
                var hasValueTrue = DefineLabel("hasValueTrue");

                Emit.Duplicate();                 // preserve value

                Emit.Call(effective.GetProperty("HasValue").GetGetMethod());

                Emit.BranchIfTrue(hasValueTrue);
                Emit.Pop();                 // discard value
                WriteConstant("null", push: false);
                Emit.Branch(ifNull);

                Emit.MarkLabel(hasValueTrue);

                Emit.Call(effective.GetMethod("GetValueOrDefault", Type.EmptyTypes));

                effective = underlying;
            }

            if (writer.MaxLength.HasValue && Destination == DestinationType.Stream)
            {
                // flush if available < writer.MaxLength
                var afterFlush = DefineLabel("afterFlush");
                Emit.LoadLocal(LocalAvailable);
                Emit.LoadConstant(writer.MaxLength.Value);
                Emit.BranchIfGreaterOrEqual(afterFlush);
                Flush(writtenTop: false, resetAvailable: true);
                Emit.MarkLabel(afterFlush);
            }

            CallWriter(writer, effective, true);

            // simple writers must flush to stream directly
            if (Destination == DestinationType.Pointer)
            {
                Emit.Duplicate();                 // preserve the written count

                // check if the write succeeded
                var success = DefineLabel("success");
                Emit.LoadConstant(0);
                Emit.BranchIfGreater(success);
                {
                    Depth++;
                    ReturnFailed();
                    Depth--;
                }

                Emit.MarkLabel(success);
            }

            if (Destination == DestinationType.Pointer || writer.MaxLength.HasValue)
            {
                Emit.Duplicate();                 // preserve written count

                // advance LocalDestination
                Emit.Convert <IntPtr>();
                Emit.LoadLocal(LocalDestination);
                Emit.Add();
                Emit.StoreLocal(LocalDestination);


                // Emit.Duplicate(); // preserve written count

                // decrement LocalAvailable
                Emit.LoadConstant(-1);
                Emit.Multiply();
                Emit.LoadLocal(LocalAvailable);
                Emit.Add();
                Emit.StoreLocal(LocalAvailable);
            }


            if (underlying != null)
            {
                Emit.MarkLabel(ifNull);
            }
        }