/// <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> /// Expects a reference to an array of the given element type and an index on the stack. /// /// Pops both, and pushes the address of the element at the given index. /// </summary> public Emit <DelegateType> LoadElementAddress(Type elementType) { if (!AllowsUnverifiableCIL) { FailUnverifiable("LoadElementAddress"); } var arrayType = elementType.MakeArrayType(); // needs to be markable so we can keep track of what makes use of this value var pushToStack = TypeOnStack.Get(elementType.MakeByRefType()); // Shove this away, later on we'll figure out if we can insert a readonly here ReadonlyPatches.Add(SigilTuple.Create(IL.Index, pushToStack)); var transitions = new[] { new StackTransition(new [] { TypeOnStack.Get <NativeIntType>(), TypeOnStack.Get(arrayType) }, new [] { pushToStack }), new StackTransition(new [] { TypeOnStack.Get <int>(), TypeOnStack.Get(arrayType) }, new [] { pushToStack }) }; UpdateState(OpCodes.Ldelema, elementType, Wrap(transitions, "LoadElementAddress")); return(this); }
private Emit(CallingConventions callConvention, Type returnType, Type[] parameterTypes, bool allowUnverifiable, bool doVerify, bool strictBranchVerification) { CallingConventions = callConvention; AllowsUnverifiableCIL = allowUnverifiable; IsVerifying = doVerify; UsesStrictBranchVerification = strictBranchVerification; ReturnType = TypeOnStack.Get(returnType); ParameterTypes = parameterTypes; IL = new BufferedILGenerator <DelegateType>(); Trackers = new LinqList <VerifiableTracker>(); AllLocals = new LinqList <Local>(); UnusedLocals = new LinqHashSet <Local>(); UnusedLabels = new LinqHashSet <Label>(); UnmarkedLabels = new LinqHashSet <Label>(); Branches = new LinqList <SigilTuple <OpCode, Label, int> >(); Marks = new LinqDictionary <Label, int>(); Returns = new LinqList <int>(); Throws = new LinqList <int>(); BranchPatches = new LinqDictionary <int, SigilTuple <Label, UpdateOpCodeDelegate, OpCode> >(); CurrentExceptionBlock = new Stack <ExceptionBlock>(); TryBlocks = new LinqDictionary <ExceptionBlock, SigilTuple <int, int> >(); CatchBlocks = new LinqDictionary <CatchBlock, SigilTuple <int, int> >(); FinallyBlocks = new LinqDictionary <FinallyBlock, SigilTuple <int, int> >(); ReadonlyPatches = new LinqList <SigilTuple <int, TypeOnStack> >(); Shorthand = new EmitShorthand <DelegateType>(this); FreedLocals = new LinqList <Local>(); CurrentLocals = new LinqDictionary <string, Local>(); Locals = new LocalLookup(CurrentLocals); CurrentLabels = new LinqDictionary <string, Label>(); Labels = new LabelLookup(CurrentLabels); ElidableCasts = new LinqList <int>(); TypesProducedAtIndex = new LinqDictionary <int, LinqList <TypeOnStack> >(); var start = DefineLabel("__start"); CurrentVerifiers = IsVerifying ? new RollingVerifier(start, UsesStrictBranchVerification) : new RollingVerifierWithoutVerification(start); MarkLabel(start); }
internal Local(object owner, ushort index, Type localType, DeclareLocallDelegate local, string name, LocalReusableDelegate reusable, int declaredAt) { _Owner = owner; LocalDel = local; Name = name; Index = index; LocalType = localType; StackType = TypeOnStack.Get(localType); Reusable = reusable; DeclaredAtIndex = declaredAt; }
Emit <DelegateType> InnerNewObject(ConstructorInfo constructor, Type[] parameterTypes) { var expectedParams = ((LinqArray <Type>)parameterTypes).Select(p => TypeOnStack.Get(p)).Reverse().ToList(); var makesType = TypeOnStack.Get(constructor.DeclaringType); var transitions = new[] { new StackTransition(expectedParams.AsEnumerable(), new [] { makesType }) }; UpdateState(OpCodes.Newobj, constructor, Wrap(transitions, "NewObject")); return(this); }
/// <summary> /// Cast a reference on the stack to the given reference type. /// /// If the cast is not legal, a CastClassException will be thrown at runtime. /// </summary> public Emit <DelegateType> CastClass(Type referenceType) { if (referenceType == null) { throw new ArgumentNullException("referenceType"); } if (referenceType.IsValueType) { throw new ArgumentException("Can only cast to ReferenceTypes, found " + referenceType); } var curIndex = IL.Index; bool elided = false; VerificationCallback before = (stack, baseless) => { // Can't reason about stack unless it's completely known if (baseless || elided) { return; } var onStack = stack.First(); if (onStack.All(a => ExtensionMethods.IsAssignableFrom(referenceType, a))) { ElidableCasts.Add(curIndex); elided = true; } }; var newType = TypeOnStack.Get(referenceType); var transitions = new[] { new StackTransition(new [] { typeof(object) }, new [] { referenceType }, before: before) }; UpdateState(OpCodes.Castclass, referenceType, Wrap(transitions, "CastClass")); return(this); }
/// <summary> /// Calls the given constructor. Pops its arguments in reverse order (left-most deepest in the stack). /// /// The `this` reference should appear before any parameters. /// </summary> public Emit <DelegateType> Call(ConstructorInfo cons) { if (cons == null) { throw new ArgumentNullException("cons"); } if (HasFlag(cons.CallingConvention, CallingConventions.VarArgs) && !HasFlag(cons.CallingConvention, CallingConventions.Standard)) { throw new NotSupportedException("Calling constructors with VarArgs is currently not supported."); } if (!IsBuildingConstructor) { throw new SigilVerificationException("Constructors may only be called directly from within a constructor, use NewObject to allocate a new object with a specific constructor.", IL.Instructions(AllLocals)); } if (!IsLegalConstructoCall(cons)) { throw new SigilVerificationException("Only constructors defined in the current class or it's base class may be called", IL.Instructions(AllLocals)); } var expectedParams = ((LinqArray <ParameterInfo>)cons.GetParameters()).Select(s => TypeOnStack.Get(s.ParameterType)).ToList(); var declaring = cons.DeclaringType; if (TypeHelpers.IsValueType(declaring)) { declaring = declaring.MakePointerType(); } expectedParams.Insert(0, TypeOnStack.Get(declaring)); var transitions = new[] { new StackTransition(expectedParams.Reverse().AsEnumerable(), new TypeOnStack[0]) }; UpdateState(OpCodes.Call, cons, ((LinqArray <ParameterInfo>)cons.GetParameters()).Select(s => s.ParameterType).AsEnumerable(), Wrap(transitions, "Call")); return(this); }
/// <summary> /// Pops # of parameters to the given constructor arguments from the stack, invokes the constructor, and pushes a reference to the new object onto the stack. /// </summary> public Emit <DelegateType> NewObject(ConstructorInfo constructor) { if (constructor == null) { throw new ArgumentNullException("constructor"); } var expectedParams = ((LinqArray <ParameterInfo>)constructor.GetParameters()).Select(p => TypeOnStack.Get(p.ParameterType)).Reverse().ToList(); var makesType = TypeOnStack.Get(constructor.DeclaringType); var transitions = new[] { new StackTransition(expectedParams.AsEnumerable(), new [] { makesType }) }; UpdateState(OpCodes.Newobj, constructor, Wrap(transitions, "NewObject")); 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); }
/// <summary> /// Calls the given method. Pops its arguments in reverse order (left-most deepest in the stack), and pushes the return value if it is non-void. /// /// If the given method is an instance method, the `this` reference should appear before any parameters. /// /// Call does not respect overrides, the implementation defined by the given MethodInfo is what will be called at runtime. /// /// To call overrides of instance methods, use CallVirtual. /// /// When calling VarArgs methods, arglist should be set to the types of the extra parameters to be passed. /// </summary> public Emit <DelegateType> Call(MethodInfo method, Type[] arglist = null) { if (method == null) { throw new ArgumentNullException("method"); } if (HasFlag(method.CallingConvention, CallingConventions.VarArgs) && !HasFlag(method.CallingConvention, CallingConventions.Standard)) { if (arglist == null) { throw new InvalidOperationException("When calling a VarArgs method, arglist must be set"); } } var expectedParams = ((LinqArray <ParameterInfo>)method.GetParameters()).Select(s => TypeOnStack.Get(s.ParameterType)).ToList(); if (arglist != null) { expectedParams.AddRange(((LinqArray <Type>)arglist).Select(t => TypeOnStack.Get(t))); } // Instance methods expect this to preceed parameters if (HasFlag(method.CallingConvention, CallingConventions.HasThis)) { var declaring = method.DeclaringType; if (TypeHelpers.IsValueType(declaring)) { declaring = declaring.MakePointerType(); } expectedParams.Insert(0, TypeOnStack.Get(declaring)); } var resultType = method.ReturnType == typeof(void) ? null : TypeOnStack.Get(method.ReturnType); var firstParamIsThis = HasFlag(method.CallingConvention, CallingConventions.HasThis) || HasFlag(method.CallingConvention, CallingConventions.ExplicitThis); IEnumerable <StackTransition> transitions; if (resultType != null) { transitions = new[] { new StackTransition(expectedParams.Reverse().AsEnumerable(), new [] { resultType }) }; } else { transitions = new[] { new StackTransition(expectedParams.Reverse().AsEnumerable(), new TypeOnStack[0]) }; } UpdateState(OpCodes.Call, method, ((LinqArray <ParameterInfo>)method.GetParameters()).Select(s => s.ParameterType).AsEnumerable(), Wrap(transitions, "Call"), firstParamIsThis: firstParamIsThis, arglist: arglist); return(this); }
/// <summary> /// <para>Calls the given method virtually. Pops its arguments in reverse order (left-most deepest in the stack), and pushes the return value if it is non-void.</para> /// <para>The `this` reference should appear before any arguments (deepest in the stack).</para> /// <para>The method invoked at runtime is determined by the type of the `this` reference.</para> /// <para>If the method invoked shouldn't vary (or if the method is static), use Call instead.</para> /// </summary> public Emit <DelegateType> CallVirtual(MethodInfo method, Type constrained = null, Type[] arglist = null) { if (method == null) { throw new ArgumentNullException("method"); } if (method.IsStatic) { throw new ArgumentException("Only non-static methods can be called using CallVirtual, found " + method); } if (HasFlag(method.CallingConvention, CallingConventions.VarArgs) && !HasFlag(method.CallingConvention, CallingConventions.Standard)) { if (arglist == null) { throw new InvalidOperationException("When calling a VarArgs method, arglist must be set"); } } var expectedParams = ((LinqArray <ParameterInfo>)method.GetParameters()).Select(s => TypeOnStack.Get(s.ParameterType)).ToList(); var declaring = method.DeclaringType; if (TypeHelpers.IsValueType(declaring)) { declaring = declaring.MakePointerType(); } // "this" parameter expectedParams.Insert(0, TypeOnStack.Get(declaring)); if (arglist != null) { expectedParams.AddRange(LinqAlternative.Select(arglist, t => TypeOnStack.Get(t))); } var resultType = method.ReturnType == typeof(void) ? null : TypeOnStack.Get(method.ReturnType); // Shove the constrained prefix in if it's supplied if (constrained != null) { UpdateState(OpCodes.Constrained, constrained, Wrap(StackTransition.None(), "CallVirtual")); } IEnumerable <StackTransition> transitions; if (resultType != null) { transitions = new[] { new StackTransition(expectedParams.Reverse().AsEnumerable(), new [] { resultType }) }; } else { transitions = new[] { new StackTransition(expectedParams.Reverse().AsEnumerable(), new TypeOnStack[0]) }; } UpdateState(OpCodes.Callvirt, method, ((LinqArray <ParameterInfo>)method.GetParameters()).Select(s => s.ParameterType).AsEnumerable(), Wrap(transitions, "CallVirtual"), arglist: arglist); return(this); }
/// <summary> /// Calls the method being constructed by the given emit. Emits so used must have been constructed with BuildMethod or related methods. /// /// Pops its arguments in reverse order (left-most deepest in the stack), and pushes the return value if it is non-void. /// /// If the given method is an instance method, the `this` reference should appear before any parameters. /// /// Call does not respect overrides, the implementation defined by the given MethodInfo is what will be called at runtime. /// /// To call overrides of instance methods, use CallVirtual. /// /// When calling VarArgs methods, arglist should be set to the types of the extra parameters to be passed. /// </summary> public Emit <DelegateType> Call <MethodEmit>(Emit <MethodEmit> emit, Type[] arglist = null) { if (emit == null) { throw new ArgumentNullException("emit"); } MethodInfo methodInfo = emit.MtdBuilder ?? (MethodInfo)emit.DynMethod; if (methodInfo == null) { throw new InvalidOperationException("emit must be building a method"); } if (HasFlag(emit.CallingConventions, CallingConventions.VarArgs) && !HasFlag(emit.CallingConventions, CallingConventions.Standard)) { if (arglist == null) { throw new InvalidOperationException("When calling a VarArgs method, arglist must be set"); } } var expectedParams = ((LinqArray <Type>)emit.ParameterTypes).Select(s => TypeOnStack.Get(s)).ToList(); if (arglist != null) { expectedParams.AddRange(((LinqArray <Type>)arglist).Select(t => TypeOnStack.Get(t))); } // Instance methods expect this to preceed parameters var declaring = methodInfo.DeclaringType; if (declaring != null) { if (HasFlag(emit.CallingConventions, CallingConventions.HasThis)) { if (declaring.IsValueType) { declaring = declaring.MakePointerType(); } expectedParams.Insert(0, TypeOnStack.Get(declaring)); } } var resultType = emit.ReturnType == TypeOnStack.Get(typeof(void)) ? null : emit.ReturnType; var firstParamIsThis = HasFlag(emit.CallingConventions, CallingConventions.HasThis) || HasFlag(emit.CallingConventions, CallingConventions.ExplicitThis); IEnumerable <StackTransition> transitions; if (resultType != null) { transitions = new[] { new StackTransition(expectedParams.Reverse().AsEnumerable(), new [] { resultType }) }; } else { transitions = new[] { new StackTransition(expectedParams.Reverse().AsEnumerable(), new TypeOnStack[0]) }; } UpdateState(OpCodes.Call, methodInfo, emit.ParameterTypes, Wrap(transitions, "Call"), firstParamIsThis: firstParamIsThis, arglist: arglist); return(this); }
/// <summary> /// Calls the given method virtually. Pops its arguments in reverse order (left-most deepest in the stack), and pushes the return value if it is non-void. /// /// The `this` reference should appear before any arguments (deepest in the stack). /// /// The method invoked at runtime is determined by the type of the `this` reference. /// /// If the method invoked shouldn't vary (or if the method is static), use Call instead. /// </summary> public Emit <DelegateType> CallVirtual(Emit <DelegateType> emit, Type constrained = null, Type[] arglist = null) { if (emit == null) { throw new ArgumentNullException("emit"); } var method = emit.MtdBuilder ?? (MethodInfo)emit.DynMethod; if (method == null) { throw new ArgumentException("Has to be a method"); } if (method.IsStatic) { throw new ArgumentException("Only non-static methods can be called using CallVirtual, found " + method); } if (HasFlag(method.CallingConvention, CallingConventions.VarArgs) && !HasFlag(method.CallingConvention, CallingConventions.Standard)) { if (arglist == null) { throw new InvalidOperationException("When calling a VarArgs method, arglist must be set"); } } var expectedParams = ((LinqArray <Type>)emit.ParameterTypes).Select(s => TypeOnStack.Get(s)).ToList(); if (arglist != null) { expectedParams.AddRange(((LinqArray <Type>)arglist).Select(t => TypeOnStack.Get(t))); } var resultType = method.ReturnType == typeof(void) ? null : TypeOnStack.Get(method.ReturnType); // Shove the constrained prefix in if it's supplied if (constrained != null) { UpdateState(OpCodes.Constrained, constrained, Wrap(StackTransition.None(), "CallVirtual")); } IEnumerable <StackTransition> transitions; if (resultType != null) { transitions = new[] { new StackTransition(expectedParams.Reverse().AsEnumerable(), new [] { resultType }) }; } else { transitions = new[] { new StackTransition(expectedParams.Reverse().AsEnumerable(), new TypeOnStack[0]) }; } UpdateState(OpCodes.Callvirt, method, emit.ParameterTypes, Wrap(transitions, "CallVirtual"), arglist: arglist); return(this); }