public void MarkLabel(Sigil.Label label) { InstructionSizes.Add(() => InstructionSize.MarkLabel()); LengthCache.Clear(); Buffer.Add( (il, logOnly, log) => { if (!logOnly) { var l = label.LabelDel(il); il.MarkLabel(l); } log.AppendLine(); log.AppendLine(label + ":"); } ); TraversableBuffer.Add(new BufferedILInstruction { MarksLabel = label }); Operations.Add(null); }
private static void BuildGetParameter(TypeBuilder typeBuilder, Type[] parameterTypes, FieldBuilder[] fields) { var get = Emit.BuildInstanceMethod(typeof(object), new[] { typeof(int), typeof(string), typeof(Type) }, typeBuilder, "GetParameter", MethodAttributes.Public | MethodAttributes.Virtual); var labels = new Sigil.Label[parameterTypes.Length]; for (var i = 0; i < labels.Length; i++) { labels[i] = get.DefineLabel(); } get.LoadArgument(1); get.Switch(labels); get.LoadArgument(2); get.NewObject <ArgumentOutOfRangeException, string>(); get.Throw(); for (var i = 0; i < parameterTypes.Length; i++) { get.MarkLabel(labels[i]); get.LoadArgument(0); get.LoadField(fields[i]); var parameterType = parameterTypes[i]; if (!parameterType.IsClass) { get.Box(parameterType); } get.Return(); } var getter = get.CreateMethod(); typeBuilder.DefineMethodOverride(getter, typeof(IServiceRemotingRequestMessageBody).GetMethod("GetParameter")); }
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(); }
public void LeaveDataOnStackBetweenBranchesNonGeneric() { var il = Emit.NewDynamicMethod(typeof(int), Type.EmptyTypes, "LeaveDataOnStackBetweenBranches"); Sigil.Label b0 = il.DefineLabel("b0"), b1 = il.DefineLabel("b1"), b2 = il.DefineLabel("b2"); il.LoadConstant("abc"); il.Branch(b0); // jump to b0 with "abc" il.MarkLabel(b1); // incoming: 3 il.LoadConstant(4); il.Call(typeof(Math).GetMethod("Max", new[] { typeof(int), typeof(int) })); il.Branch(b2); // jump to b2 with 4 il.MarkLabel(b0); // incoming: "abc" il.CallVirtual(typeof(string).GetProperty("Length").GetGetMethod()); il.Branch(b1); // jump to b1 with 3 il.MarkLabel(b2); // incoming: 4 il.Return(); int i = il.CreateDelegate <Func <int> >()(); Assert.AreEqual(4, i); }
public void Emit(OpCode op, Sigil.Label label, out UpdateOpCodeDelegate update) { var localOp = op; update = newOpcode => { LengthCache.Clear(); localOp = newOpcode; }; InstructionSizes.Add(() => InstructionSize.Get(localOp)); LengthCache.Clear(); Buffer.Add( (il, logOnly, log) => { if (!logOnly) { var l = label.LabelDel(il); il.Emit(localOp, l); } log.AppendLine(localOp + " " + label); } ); TraversableBuffer.Add(new BufferedILInstruction { IsInstruction = op }); Operations.Add(new Operation <DelegateType> { OpCode = op, Parameters = new object[] { label } }); }
void EmitObject(Schema schema, bool pushResult = false) { // merge consecutive constant strings var spans = new List <KeyValuePair <string, Schema> >(); for (int i = 0; i < schema.Members.Count; i++) { Append(spans, (i == 0 ? "{\"" : ",\"") + Escape(schema.Members[i].Key) + "\":"); string constant = null; var member = schema.Members[i].Value; var prop = member.PropertyInfo; if (prop != null) { constant = ConstantMethods.TryGetJson(prop.GetMethod, schema.NetType); } if (constant != null) { Append(spans, constant); } else { spans.Add(new KeyValuePair <string, Schema>(null, member)); } if (i == schema.Members.Count - 1) { Append(spans, "}"); } } var underlying = Nullable.GetUnderlyingType(schema.NetType); var systemNullable = underlying != null; Sigil.Label ifNull = null; bool anyMembers = spans.Any(span => span.Value != null); if (schema.Nullable) { var ifNotNull = DefineLabel("IfNotNull"); ifNull = DefineLabel("ifNull"); if (anyMembers) { Emit.Duplicate(); // preserve the value } if (systemNullable) { Emit.Call(schema.NetType.GetProperty("HasValue").GetMethod); } Emit.BranchIfTrue(ifNotNull); if (anyMembers) { Emit.Pop(); // discard value } if (pushResult) { Emit.Pop(); // discard avail } WriteConstant("null", push: pushResult); Emit.Branch(ifNull); Emit.MarkLabel(ifNotNull); if (systemNullable) { Emit.Call(schema.NetType.GetMethod("GetValueOrDefault", Type.EmptyTypes)); } } if (schema.Members.Count == 0) { if (pushResult) { Depth++; } WriteConstant("{}"); if (pushResult) { Depth--; } } else { int lastNonConstant = -1; int minLength = 0; // find last non constant and min length for (int i = 0; i < spans.Count; i++) { var span = spans[i]; if (span.Key != null) { minLength += Encoding.UTF8.GetByteCount(span.Key); } else { lastNonConstant = i; minLength += span.Value.CalculateMinimumLength(considerNullMembers: true); } } for (int i = 0; i < spans.Count; i++) { var span = spans[i]; // assert we've got enough var enoughAvailable = DefineLabel("enoughAvailable"); Emit.LoadLocal(LocalAvailable); Emit.LoadConstant(minLength); Emit.BranchIfGreaterOrEqual(enoughAvailable); { int extra = pushResult ? 2 : 1; if (i > lastNonConstant) { extra--; } Depth += extra; ReturnFailed(); Depth -= extra; } Emit.MarkLabel(enoughAvailable); if (span.Key != null) { int written = WriteConstant(span.Key, assertAvailable: false); minLength -= written; continue; } var member = span.Value; minLength -= member.CalculateMinimumLength(considerNullSelf: true); var lastMember = i == lastNonConstant; if (!lastMember) { // sanity check minLength if (minLength < 6) // ,"a":0 { // FIXME use/create appropriate Exception type throw new InvalidOperationException("Minimum length calculation failed"); } // preserve value except last time through the loop Emit.Duplicate(); } int loopDepth = (lastMember ? 1 : 2) - (pushResult ? 0 : 1); Depth += loopDepth; // get the member value if (member.FieldInfo != null) { Emit.LoadField(member.FieldInfo); } else { if (schema.NetType.IsValueType) { PushAddress(systemNullable ? underlying : schema.NetType); } Emit.Call(member.PropertyInfo.GetMethod); } // write the member value EmitInline(member); Depth -= loopDepth; // if we ran out of room inner writer will have bailed completely rather than push negative (currently) // we probably won't care about result until we create more fine grained estimates when out of room } } if (pushResult) { // push bytes written Emit.LoadLocal(LocalAvailable); Emit.Subtract(); } if (schema.Nullable) { Emit.MarkLabel(ifNull); } }
// 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); } }