public static bool IsAssignableFrom(this TypeOnStack type1, TypeOnStack type2) { // Native int can be convereted to any pointer type if (type1.IsPointer && type2 == TypeOnStack.Get<NativeIntType>()) return true; if (type2.IsPointer && type1 == TypeOnStack.Get<NativeIntType>()) return true; if ((type1.IsPointer || type1.IsReference) && !(type2.IsPointer || type2.IsReference)) return false; if ((type2.IsPointer || type2.IsReference) && !(type1.IsPointer || type1.IsReference)) return false; if (type1.IsPointer || type1.IsReference) { return type1.Type.GetElementType() == type2.Type.GetElementType(); } var t1 = type1.Type; var t2 = type2.Type; t1 = Alias(t1); t2 = Alias(t2); // The null type can be assigned to any reference type if (t2 == typeof(NullType) && !t1.IsValueType) return true; return ReallyIsAssignableFrom(t1, t2); }
/// <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> /// Pushes a pointer to the given function onto the stack, as a native int. /// /// To resolve a method at runtime using an object, use LoadVirtualFunctionPointer instead. /// </summary> public Emit <DelegateType> LoadFunctionPointer(MethodInfo method) { if (method == null) { throw new ArgumentNullException("method"); } var parameters = method.GetParameters(); var paramList = ((LinqArray <ParameterInfo>)parameters).Select(p => p.ParameterType).ToList(); var type = TypeOnStack.GetKnownFunctionPointer( method.CallingConvention, HasFlag(method.CallingConvention, CallingConventions.HasThis) ? method.DeclaringType : null, method.ReturnType, paramList.ToArray() ); UpdateState(OpCodes.Ldftn, method, paramList.AsEnumerable(), Wrap(new[] { new StackTransition(new TypeOnStack[0], new[] { type }) }, "LoadFunctionPointer")); return(this); }
Emit <DelegateType> InnerLoadVirtualFunctionPointer(MethodInfo method, Type[] parameterTypes) { var thisType = HasFlag(method.CallingConvention, CallingConventions.HasThis) ? method.DeclaringType : null; var paramList = new List <Type>(parameterTypes); var declaring = method.DeclaringType; if (TypeHelpers.IsValueType(declaring)) { declaring = declaring.MakePointerType(); } paramList.Insert(0, declaring); var type = TypeOnStack.GetKnownFunctionPointer( method.CallingConvention, thisType, method.ReturnType, paramList.ToArray() ); var transitions = new[] { new StackTransition(new [] { declaring }, new [] { typeof(NativeIntType) }) }; UpdateState(OpCodes.Ldvirtftn, method, parameterTypes, Wrap(transitions, "LoadVirtualFunctionPointer")); 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); }
public static bool IsAssignableFrom(TypeOnStack type1, TypeOnStack type2) { // wildcards match *everything* if (type1 == TypeOnStack.Get<WildcardType>() || type2 == TypeOnStack.Get<WildcardType>()) return true; if (type1.IsArray && type2.IsArray) { if (type1.Type.GetArrayRank() == type2.Type.GetArrayRank()) { var t1Elem = type1.Type.GetElementType(); var t2Elem = type2.Type.GetElementType(); while (t1Elem.HasElementType) t1Elem = t1Elem.GetElementType(); while (t2Elem.HasElementType) t2Elem = t2Elem.GetElementType(); if (t1Elem == typeof(WildcardType) || t2Elem == typeof(WildcardType)) return true; } } if (type1.IsPointer && type2.IsPointer) { if (type1.Type.GetElementType() == typeof(WildcardType) || type2.Type.GetElementType() == typeof(WildcardType)) return true; } if (type1.IsReference && type2.IsReference) { if (type1.Type.GetElementType() == typeof(WildcardType) || type2.Type.GetElementType() == typeof(WildcardType)) return true; } // any pointer type matches, well, any pointer if (type1.Type == typeof(AnyPointerType) && type2.IsPointer) return true; if (type2.Type == typeof(AnyPointerType) && type1.IsPointer) return true; // likewise for any by ref if (type1.Type == typeof(AnyByRefType) && type2.IsReference) return true; if (type2.Type == typeof(AnyByRefType) && type1.IsReference) return true; // Native int can be convereted to any pointer type if (type1.IsPointer && type2 == TypeOnStack.Get<NativeIntType>()) return true; if (type2.IsPointer && type1 == TypeOnStack.Get<NativeIntType>()) return true; if ((type1.IsPointer || type1.IsReference) && !(type2.IsPointer || type2.IsReference)) return false; if ((type2.IsPointer || type2.IsReference) && !(type1.IsPointer || type1.IsReference)) return false; if (type1.IsPointer || type1.IsReference) { return type1.Type.GetElementType() == type2.Type.GetElementType(); } var t1 = type1.Type; var t2 = type2.Type; // The null type can be assigned to any reference type if (t1 == typeof(NullType) && !t2.IsValueType) return true; if (t2 == typeof(NullType) && !t1.IsValueType) return true; t1 = Alias(t1); t2 = Alias(t2); return ReallyIsAssignableFrom(t1, t2); }
public static bool IsAssignableFrom(Type type1, TypeOnStack type2) { return TypeOnStack.Get(type1).IsAssignableFrom(type2); }
private StackState(StackState prev, TypeOnStack val) { Previous = prev; Value = val; }
public TypeOnStack[] Top(int n = 1) { var ret = new TypeOnStack[n]; int i = 0; var cur = this; while (i < n) { if (cur.IsRoot) return null; ret[i] = cur.Value; cur = cur.Previous; i++; } return ret; }
public StackState Push(TypeOnStack val) { return new StackState(this, val); }
/// <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); }