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); }
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); }