internal static WriteToStream <T> CacheStreamDelegate <T>()
        {
            var schema = Schema.Reflect(typeof(T));
            var emit   = DelegateBuilder.CreateStream <T>(schema);
            var del    = emit.CreateDelegate <WriteToStream <T> >();

            lock (_StreamCached)
            {
                _StreamCached[typeof(T)] = del;
            }
            return(del);
        }
Example #2
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);
        }