/// <summary> /// Ends the execution of the current method. /// /// If the current method does not return void, pops a value from the stack and returns it to the calling method. /// /// Return should leave the stack empty. /// </summary> public Emit <DelegateType> Return() { if (ReturnType == TypeOnStack.Get(typeof(void))) { UpdateState(Wrap(new[] { new StackTransition(0) }, "Return")); UpdateState(OpCodes.Ret, Wrap(StackTransition.None(), "Return")); Returns.Add(IL.Index); MustMark = true; return(this); } UpdateState(OpCodes.Ret, Wrap(StackTransition.Pop(ReturnType), "Return")); Returns.Add(IL.Index); UpdateState(Wrap(new[] { new StackTransition(0) }, "Return")); MustMark = true; var verify = CurrentVerifiers.Return(); if (!verify.Success) { throw new SigilVerificationException("Return", verify, IL.Instructions(AllLocals)); } return(this); }
/// <summary> /// <para>Pops a value off the stack and branches to the label at the index of that value in the given labels.</para> /// <para>If the value is out of range, execution falls through to the next instruction.</para> /// </summary> public Emit <DelegateType> Switch(params Label[] labels) { if (labels == null) { throw new ArgumentNullException("labels"); } if (labels.Length == 0) { throw new ArgumentException("labels must have at least one element"); } if (LinqAlternative.Any(labels, l => ((IOwned)l).Owner is DisassembledOperations <DelegateType>)) { return (Switch(LinqAlternative.Select(labels, l => l.Name).ToArray())); } foreach (var label in labels) { if (((IOwned)label).Owner != this) { FailOwnership(label); } } foreach (var label in labels) { UnusedLabels.Remove(label); } var transitions = new[] { new StackTransition(new [] { typeof(int) }, TypeHelpers.EmptyTypes), new StackTransition(new [] { typeof(NativeIntType) }, TypeHelpers.EmptyTypes), }; var labelsCopy = ((LinqArray <Label>)labels).Select(l => l).ToArray(); UpdateOpCodeDelegate update; UpdateState(OpCodes.Switch, labelsCopy, Wrap(transitions, "Switch"), out update); var valid = CurrentVerifiers.ConditionalBranch(labels); if (!valid.Success) { throw new SigilVerificationException("Switch", valid, IL.Instructions(AllLocals)); } foreach (var label in labels) { Branches.Add(SigilTuple.Create(OpCodes.Switch, label, IL.Index)); BranchPatches[IL.Index] = SigilTuple.Create(label, update, OpCodes.Switch); } return(this); }
/// <summary> /// Pops a value off the stack and throws it as an exception. /// /// Throw expects the value to be or extend from a System.Exception. /// </summary> public Emit <DelegateType> Throw() { UpdateState(OpCodes.Throw, Wrap(StackTransition.Pop <Exception>(), "Throw")); UpdateState(Wrap(new[] { new StackTransition(new[] { typeof(PopAllType) }, TypeHelpers.EmptyTypes) }, "Throw")); Throws.Add(IL.Index); MustMark = true; var verify = CurrentVerifiers.Throw(); if (!verify.Success) { throw new SigilVerificationException("Throw", verify, IL.Instructions(AllLocals)); } return(this); }
/// <summary> /// Marks a label in the instruction stream. /// /// When branching, leaving, or switching with a label control will be transfered to where it was *marked* not defined. /// /// Labels can only be marked once, and *must* be marked before creating a delegate. /// </summary> public Emit <DelegateType> MarkLabel(Label label) { if (label == null) { throw new ArgumentNullException("label"); } if (((IOwned)label).Owner != this) { if (((IOwned)label).Owner is DisassembledOperations <DelegateType> ) { return(MarkLabel(label.Name)); } FailOwnership(label); } if (!UnmarkedLabels.Contains(label)) { throw new InvalidOperationException("label [" + label.Name + "] has already been marked, and cannot be marked a second time"); } if (MustMark) { MustMark = false; } var valid = CurrentVerifiers.Mark(label); if (!valid.Success) { throw new SigilVerificationException("MarkLabel", valid, IL.Instructions(AllLocals)); } UnmarkedLabels.Remove(label); IL.MarkLabel(label); Marks[label] = IL.Index; return(this); }
/// <summary> /// <para>Pops one argument from the stack, branches to the given label if the value is true.</para> /// <para>A value is true if it is non-zero or non-null.</para> /// </summary> public Emit <DelegateType> BranchIfTrue(Label label) { if (label == null) { throw new ArgumentNullException("label"); } if (((IOwned)label).Owner != this) { if (((IOwned)label).Owner is DisassembledOperations <DelegateType> ) { return(BranchIfTrue(label.Name)); } FailOwnership(label); } UnusedLabels.Remove(label); var transitions = new[] { new StackTransition(new [] { typeof(WildcardType) }, TypeHelpers.EmptyTypes) }; UpdateOpCodeDelegate update; UpdateState(OpCodes.Brtrue, label, Wrap(transitions, "BranchIfTrue"), out update); var valid = CurrentVerifiers.ConditionalBranch(label); if (!valid.Success) { throw new SigilVerificationException("BranchIfTrue", valid, IL.Instructions(AllLocals)); } Branches.Add(SigilTuple.Create(OpCodes.Brtrue, label, IL.Index)); BranchPatches[IL.Index] = SigilTuple.Create(label, update, OpCodes.Brtrue); return(this); }
/// <summary> /// <para>Leave an exception or catch block, branching to the given label.</para> /// <para>This instruction empties the stack.</para> /// </summary> public Emit <DelegateType> Leave(Label label) { if (label == null) { throw new ArgumentNullException("label"); } if (((IOwned)label).Owner != this) { if (((IOwned)label).Owner is DisassembledOperations <DelegateType> ) { return(Leave(label.Name)); } FailOwnership(label); } if (!TryBlocks.Any(t => t.Value.Item2 == -1) && !CatchBlocks.Any(c => c.Value.Item2 == -1)) { throw new InvalidOperationException("Leave can only be used within an exception or catch block"); } // Note that Leave *always* nuked the stack; nothing survies exiting an exception block UpdateOpCodeDelegate update; UpdateState(OpCodes.Leave, label, Wrap(new[] { new StackTransition(new [] { typeof(PopAllType) }, TypeHelpers.EmptyTypes) }, "Leave"), out update); Branches.Add(SigilTuple.Create(OpCodes.Leave, label, IL.Index)); BranchPatches[IL.Index] = SigilTuple.Create(label, update, OpCodes.Leave); MustMark = true; var valid = CurrentVerifiers.UnconditionalBranch(label); if (!valid.Success) { throw new SigilVerificationException("Leave", valid, IL.Instructions(AllLocals)); } return(this); }
/// <summary> /// Unconditionally branches to the given label. /// </summary> public Emit <DelegateType> Branch(Label label) { if (label == null) { throw new ArgumentNullException("label"); } if (((IOwned)label).Owner != this) { if (((IOwned)label).Owner is DisassembledOperations <DelegateType> ) { return(Branch(label.Name)); } FailOwnership(label); } UnusedLabels.Remove(label); UpdateOpCodeDelegate update; UpdateState(OpCodes.Br, label, Wrap(StackTransition.None(), "Branch"), out update); var valid = CurrentVerifiers.UnconditionalBranch(label); if (!valid.Success) { throw new SigilVerificationException("Branch", valid, IL.Instructions(AllLocals)); } Branches.Add(SigilTuple.Create(OpCodes.Br, label, IL.Index)); BranchPatches[IL.Index] = SigilTuple.Create(label, update, OpCodes.Br); MustMark = true; return(this); }
/// <summary> /// From within a catch block, rethrows the exception that caused the catch block to be entered. /// </summary> public Emit <DelegateType> ReThrow() { if (!CatchBlocks.Any(c => c.Value.Item2 == -1)) { throw new InvalidOperationException("ReThrow is only legal in a catch block"); } UpdateState(OpCodes.Rethrow, Wrap(StackTransition.None(), "ReThrow")); UpdateState(Wrap(new[] { new StackTransition(new[] { typeof(PopAllType) }, Type.EmptyTypes) }, "ReThrow")); Throws.Add(IL.Index); MustMark = true; var verify = CurrentVerifiers.ReThrow(); if (!verify.Success) { throw new SigilVerificationException("ReThrow", verify, IL.Instructions(AllLocals)); } return(this); }
/// <summary> /// Pops two arguments from the stack, branches to the given label if the second value is less than the first value (when treated as unsigned values). /// </summary> public Emit <DelegateType> UnsignedBranchIfLess(Label label) { if (label == null) { throw new ArgumentNullException("label"); } if (((IOwned)label).Owner != this) { if (((IOwned)label).Owner is DisassembledOperations <DelegateType> ) { return(UnsignedBranchIfLess(label.Name)); } FailOwnership(label); } UnusedLabels.Remove(label); UpdateOpCodeDelegate update; UpdateState(OpCodes.Blt_Un, label, BranchComparableTransitions("UnsignedBranchIfLess"), out update); var valid = CurrentVerifiers.ConditionalBranch(label); if (!valid.Success) { throw new SigilVerificationException("UnsignedBranchIfLess", valid, IL.Instructions(AllLocals)); } Branches.Add(SigilTuple.Create(OpCodes.Blt_Un, label, IL.Index)); BranchPatches[IL.Index] = SigilTuple.Create(label, update, OpCodes.Blt_Un); return(this); }
/// <summary> /// <para> /// Pops a pointer to a method, and then all it's arguments (in reverse order, left-most parameter is deepest on the stack) and calls /// invokes the method pointer. If the method returns a non-void result, it is pushed onto the stack. /// </para> /// <para>This override allows an arglist to be passed for calling VarArgs methods.</para> /// </summary> public Emit <DelegateType> CallIndirect(CallingConventions callConventions, Type returnType, Type[] parameterTypes, Type[] arglist = null) { if (returnType == null) { throw new ArgumentNullException("returnType"); } if (parameterTypes == null) { throw new ArgumentNullException("parameterTypes"); } var known = CallingConventions.Any | CallingConventions.ExplicitThis | CallingConventions.HasThis | CallingConventions.Standard | CallingConventions.VarArgs; known = ~known; if ((callConventions & known) != 0) { throw new ArgumentException("Unexpected value not in CallingConventions", "callConventions"); } if (!AllowsUnverifiableCIL) { FailUnverifiable("CallIndirect"); } if (HasFlag(callConventions, CallingConventions.VarArgs) && !HasFlag(callConventions, CallingConventions.Standard)) { if (arglist == null) { throw new InvalidOperationException("When calling a VarArgs method, arglist must be set"); } } var takeExtra = 1; if (HasFlag(callConventions, CallingConventions.HasThis)) { takeExtra++; } IEnumerable <StackTransition> transitions; if (HasFlag(callConventions, CallingConventions.HasThis)) { var p = new List <Type>(); p.Add(typeof(NativeIntType)); p.AddRange(LinqAlternative.Reverse(parameterTypes).AsEnumerable()); p.Add(typeof(WildcardType)); if (returnType != typeof(void)) { transitions = new[] { new StackTransition(p, new [] { returnType }) }; } else { transitions = new[] { new StackTransition(p, TypeHelpers.EmptyTypes) }; } } else { var p = new List <Type>(); p.Add(typeof(NativeIntType)); p.AddRange(LinqAlternative.Reverse(parameterTypes).AsEnumerable()); if (returnType != typeof(void)) { transitions = new[] { new StackTransition(p, new [] { returnType }) }; } else { transitions = new[] { new StackTransition(p, TypeHelpers.EmptyTypes) }; } } var onStack = CurrentVerifiers.InferStack(LinqAlternative.ElementAt(transitions, 0).PoppedFromStack.Length); if (onStack != null && onStack.Count > 0) { var funcPtr = onStack.First(); if (funcPtr == TypeOnStack.Get <NativeIntType>() && funcPtr.HasAttachedMethodInfo) { if (funcPtr.CallingConvention != callConventions) { throw new SigilVerificationException("CallIndirect expects method calling conventions to match, found " + funcPtr.CallingConvention + " on the stack", IL.Instructions(AllLocals)); } if (HasFlag(callConventions, CallingConventions.HasThis)) { var thisRef = onStack.Last(); if (!ExtensionMethods.IsAssignableFrom(funcPtr.InstanceType, thisRef)) { throw new SigilVerificationException("CallIndirect expects a 'this' value assignable to " + funcPtr.InstanceType + ", found " + thisRef, IL.Instructions(AllLocals)); } } if (funcPtr.ReturnType != returnType) { throw new SigilVerificationException("CallIndirect expects method return types to match, found " + funcPtr.ReturnType + " on the stack", IL.Instructions(AllLocals)); } } } UpdateState(OpCodes.Calli, callConventions, returnType, parameterTypes, Wrap(transitions, "CallIndirect"), arglist); return(this); }