void AddGuards() { foreach (var method in TargetType.Methods) { if (method.Name == ".ctor") { continue; } if (method.IsMatch("Finalize")) { continue; } if (method.IsStatic) { continue; } if (method.Name.StartsWith("Dispose")) { continue; } if (method.Name == "ThrowIfDisposed") { continue; } if (method.Name == "IsDisposed") { continue; } if (!method.HasBody) { continue; } if (method.IsPrivate) { continue; } var validSequencePoint = method.DebugInformation.SequencePoints.FirstOrDefault(); method.Body.SimplifyMacros(); var instructions = method.Body.Instructions; instructions.InsertAtStart( Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Call, throwIfDisposed)); if (validSequencePoint != null) { CecilExtensions.HideLineFromDebugger(validSequencePoint); } method.Body.OptimizeMacros(); } }
public void should_return_false_on_null() { var context = new ModuleWeavingContext(null !, null !); CecilExtensions.IsInlineILTypeUsage(default(CustomAttribute), context).ShouldBeFalse(); CecilExtensions.IsInlineILTypeUsage(default(FieldReference), context).ShouldBeFalse(); CecilExtensions.IsInlineILTypeUsage(default(IMethodSignature), context).ShouldBeFalse(); CecilExtensions.IsInlineILTypeUsage(default(ParameterDefinition), context).ShouldBeFalse(); CecilExtensions.IsInlineILTypeUsage(default(TypeReference), context).ShouldBeFalse(); CecilExtensions.IsInlineILTypeUsage(default(EventReference), context).ShouldBeFalse(); CecilExtensions.IsInlineILTypeUsage(default(PropertyReference), context).ShouldBeFalse(); CecilExtensions.IsInlineILTypeUsage(default(InterfaceImplementation), context).ShouldBeFalse(); CecilExtensions.IsInlineILTypeUsage(default(GenericParameterConstraint), context).ShouldBeFalse(); CecilExtensions.IsInlineILTypeUsageDeep(null, context).ShouldBeFalse(); }
//Check and enforce some restriction for IBufferElementData: // "Buffer element MUST have all fields annotated with the GhostFieldAttribute" // //This rule must be enforced to avoid having uninitialized data when a dynamic buffer is restored from the history //(see GhostPredictionHistorySystem.cs). //When a buffer is restored it might get resized and in that case, since we don't clear the buffer memory (for performance reason), //some portion of the data could be left untouched by the RestoreFromBackup function if some element fields are are not replicated. //The above requirement avoid that problem. We migh relax it later. private TypeDefinition[] CheckAndFilterInvalidGhostTypes(TypeDefinition[] ghostTypes) { bool CheckIsValid(TypeDefinition t) { if (CecilExtensions.IsBufferElementData(t) && t.Fields.Any(f => f.IsPublic && !f.HasAttribute <GhostFieldAttribute>())) { Debug.LogError($"BufferElementData {t.FullName} has some members without a GhostField attribute.\n" + "In order to be replicated, BufferElementData requires that all fields must be annotated with a GhostField attribute."); return(false); } return(true); } return(ghostTypes.Where(CheckIsValid).ToArray()); }
public static void WriteTo(this Instruction instruction, ITextOutput writer) { writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction); writer.Write(": "); writer.WriteReference(instruction.OpCode.Name, instruction.OpCode); if (instruction.Operand != null) { writer.Write(' '); if (instruction.OpCode == OpCodes.Ldtoken) { if (instruction.Operand is MethodReference) { writer.Write("method "); } else if (instruction.Operand is FieldReference) { writer.Write("field "); } } WriteOperand(writer, instruction.Operand); } }
public SymbolicValue Eval(ILExpression expr) { SymbolicValue left, right; switch (expr.Code) { case ILCode.Sub: left = Eval(expr.Arguments[0]); right = Eval(expr.Arguments[1]); if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant) { return(Failed()); } if (right.Type != SymbolicValueType.IntegerConstant) { return(Failed()); } return(new SymbolicValue(left.Type, unchecked (left.Constant - right.Constant))); case ILCode.Ldfld: if (Eval(expr.Arguments[0]).Type != SymbolicValueType.This) { return(Failed()); } if (CecilExtensions.ResolveWithinSameModule(expr.Operand as FieldReference) != stateField) { return(Failed()); } return(new SymbolicValue(SymbolicValueType.State)); case ILCode.Ldloc: ILVariable loadedVariable = (ILVariable)expr.Operand; if (stateVariables.Contains(loadedVariable)) { return(new SymbolicValue(SymbolicValueType.State)); } else if (loadedVariable.IsParameter && loadedVariable.OriginalParameter.Index < 0) { return(new SymbolicValue(SymbolicValueType.This)); } else { return(Failed()); } case ILCode.Ldc_I4: return(new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand)); case ILCode.Ceq: case ILCode.Cne: left = Eval(expr.Arguments[0]); right = Eval(expr.Arguments[1]); if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant) { return(Failed()); } // bool: (state + left.Constant == right.Constant) // bool: (state == right.Constant - left.Constant) return(new SymbolicValue(expr.Code == ILCode.Ceq ? SymbolicValueType.StateEquals : SymbolicValueType.StateInEquals, unchecked (right.Constant - left.Constant))); case ILCode.LogicNot: SymbolicValue val = Eval(expr.Arguments[0]); if (val.Type == SymbolicValueType.StateEquals) { return(new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant)); } else if (val.Type == SymbolicValueType.StateInEquals) { return(new SymbolicValue(SymbolicValueType.StateEquals, val.Constant)); } else { return(Failed()); } default: return(Failed()); } }
void PrepareClassMethods(Type type) { var @class = GetClass(type); // Already processed? if (@class == null || @class.MethodCompiled) { return; } @class.MethodCompiled = true; // Array: no need to do anything (its type definition, Array, has already been processed) if (@class.Type.TypeReference is ArrayType) { return; } var typeDefinition = GetMethodTypeDefinition(@class.Type.TypeReference); bool isInterface = typeDefinition.IsInterface; // Process methods, Virtual first, then non virtual, then static foreach (var method in typeDefinition.Methods.OrderBy(x => x.IsVirtual ? 0 : (!x.IsStatic ? 1 : 2))) { var methodReference = ResolveGenericMethod(@class.Type.TypeReference, method); // If a method contains generic parameters, skip it // Its closed instantiations (with generic arguments) is what needs to be generated. // (except interface methods) // Using ResolveGenericsVisitor.ContainsGenericParameters because Cecil one's doesn't seem to match what .NET Type does. // TODO: Might need a more robust generic resolver/analyzer system soon. if (ResolveGenericsVisitor.ContainsGenericParameters(methodReference)) { continue; } var function = CreateFunction(methodReference); @class.Functions.Add(function); if (method.IsSpecialName && method.Name == ".cctor") { @class.StaticCtor = function; } if (method.IsVirtual) { if (isInterface) { // Store IMT slot function.VirtualSlot = (int)(GetMethodId(methodReference) % InterfaceMethodTableSize); } else if (method.IsNewSlot) { // New slot function.VirtualSlot = @class.VirtualTable.Count; @class.VirtualTable.Add(function); } else { // Find slot in base types var baseType = @class.BaseType; Function matchedMethod = null; while (baseType != null) { matchedMethod = CecilExtensions.TryMatchMethod(baseType, methodReference); if (matchedMethod != null) { break; } baseType = baseType.BaseType; } if (matchedMethod == null) { throw new InvalidOperationException(string.Format("Could not find a slot for virtual function {0} in parents of class {1}", method, @class.Type.TypeReference)); } function.VirtualSlot = matchedMethod.VirtualSlot; @class.VirtualTable[function.VirtualSlot] = function; } } else { // New slot function.VirtualSlot = @class.VirtualTable.Count; @class.VirtualTable.Add(function); } } }
public void WriteTo(TextWriter writer) { foreach (Instruction prefix in this.Prefixes) { Disassembler.DisassemblerHelpers.WriteTo(prefix, new PlainTextOutput(writer)); writer.WriteLine(); } if (Instruction != null && Instruction.Offset >= 0) { writer.Write(CecilExtensions.OffsetToString(Instruction.Offset)); writer.Write(": "); } if (Target != null) { writer.Write(Target.ToString()); writer.Write(" = "); } if (IsMoveInstruction) { writer.Write(Operands[0].ToString()); if (Instruction != null) { writer.Write(" (" + Instruction.OpCode.Name + ")"); } } else { if (Instruction == null) { writer.Write(SpecialOpCode.ToString()); } else { writer.Write(Instruction.OpCode.Name); if (null != Instruction.Operand) { writer.Write(' '); Disassembler.DisassemblerHelpers.WriteOperand(new PlainTextOutput(writer), Instruction.Operand); writer.Write(' '); } } if (TypeOperand != null) { writer.Write(' '); writer.Write(TypeOperand.ToString()); writer.Write(' '); } if (Operands.Length > 0) { writer.Write('('); for (int i = 0; i < Operands.Length; i++) { if (i > 0) { writer.Write(", "); } writer.Write(Operands[i].ToString()); } writer.Write(')'); } } }
void CompileClassMethods(Class @class) { // Already processed? if (@class.MethodCompiled) { return; } @class.MethodCompiled = true; var typeDefinition = @class.TypeReference.Resolve(); bool isInterface = typeDefinition.IsInterface; // Process methods foreach (var method in typeDefinition.Methods) { // If a method contains generic parameters, skip it // Its closed instantiations (with generic arguments) is what needs to be generated. if (method.ContainsGenericParameter()) { continue; } var methodReference = ResolveGenericMethod(@class.TypeReference, method); var function = CreateFunction(methodReference); @class.Functions.Add(function); if (method.IsVirtual) { if (isInterface) { // Store IMT slot function.VirtualSlot = (int)(GetMethodId(methodReference) % InterfaceMethodTableSize); } else if (method.IsNewSlot) { // New slot function.VirtualSlot = @class.VirtualTable.Count; @class.VirtualTable.Add(function); } else { // Find slot in base types var baseType = @class.BaseType; Function matchedMethod = null; while (baseType != null) { matchedMethod = CecilExtensions.TryMatchMethod(baseType, method); if (matchedMethod != null) { break; } baseType = baseType.BaseType; } if (matchedMethod == null) { throw new InvalidOperationException(string.Format("Could not find a slot for virtual function {0} in parents of class {1}", method, @class.TypeReference)); } function.VirtualSlot = matchedMethod.VirtualSlot; @class.VirtualTable[function.VirtualSlot] = function; } } } }
private void BuildRuntimeType(Class @class) { if (@class.IsEmitted) { return; } @class.IsEmitted = true; // Build IMT var interfaceMethodTable = new LinkedList <InterfaceMethodTableEntry> [InterfaceMethodTableSize]; foreach (var @interface in @class.Interfaces) { foreach (var interfaceMethod in @interface.Type.TypeReference.Resolve().Methods) { var resolvedInterfaceMethod = ResolveGenericMethod(@interface.Type.TypeReference, interfaceMethod); // If method is not fully resolved (generic method in interface), ignore it // We are waiting for actual closed uses. if (ResolveGenericsVisitor.ContainsGenericParameters(resolvedInterfaceMethod)) { continue; } var resolvedFunction = CecilExtensions.TryMatchMethod(@class, resolvedInterfaceMethod); if (resolvedFunction == null && @class.Type.TypeReference is ArrayType) { var arrayType = corlib.MainModule.GetType(typeof(Array).FullName); var matchingMethod = (MethodReference)arrayType.Methods.First(x => x.Name.StartsWith("InternalArray_") && x.Name.EndsWith(resolvedInterfaceMethod.Name)); if (matchingMethod != null) { if (matchingMethod.HasGenericParameters) { matchingMethod = matchingMethod.MakeGenericMethod(((ArrayType)@class.Type.TypeReference).ElementType); } resolvedFunction = GetFunction(matchingMethod); // Manually emit Array functions locally (until proper mscorlib + generic instantiation exists). EmitFunction(resolvedFunction); } } if (resolvedFunction == null) { throw new InvalidOperationException(string.Format("Could not find matching method for {0} in {1}", resolvedInterfaceMethod, @class)); } var isInterface = resolvedFunction.DeclaringType.TypeReference.Resolve().IsInterface; if (!isInterface && resolvedFunction.MethodReference.Resolve().IsVirtual&& resolvedFunction.VirtualSlot != -1) { // We might have found a base virtual method matching this interface method. // Let's get the actual method override for this virtual slot. resolvedFunction = @class.VirtualTable[resolvedFunction.VirtualSlot]; } // If method is not found, it could be due to covariance/contravariance if (resolvedFunction == null) { throw new InvalidOperationException("Interface method not found"); } var methodId = GetMethodId(resolvedInterfaceMethod); var imtSlotIndex = (int)(methodId % interfaceMethodTable.Length); var imtSlot = interfaceMethodTable[imtSlotIndex]; if (imtSlot == null) { interfaceMethodTable[imtSlotIndex] = imtSlot = new LinkedList <InterfaceMethodTableEntry>(); } imtSlot.AddLast(new InterfaceMethodTableEntry { Function = resolvedFunction, MethodId = methodId, SlotIndex = imtSlotIndex }); } } var interfaceMethodTableConstant = LLVM.ConstArray(intPtrType, interfaceMethodTable.Select(imtSlot => { if (imtSlot == null) { // No entries: null slot return(LLVM.ConstNull(intPtrType)); } if (imtSlot.Count == 1) { // Single entry var imtEntry = imtSlot.First.Value; return(LLVM.ConstPointerCast(imtEntry.Function.GeneratedValue, intPtrType)); } else { // Multiple entries, create IMT array with all entries // TODO: Support covariance/contravariance? var imtEntries = LLVM.ConstArray(imtEntryType, imtSlot.Select(imtEntry => { return(LLVM.ConstNamedStruct(imtEntryType, new[] { LLVM.ConstInt(int32Type, (ulong)imtEntry.MethodId, false), // i32 functionId LLVM.ConstPointerCast(imtEntry.Function.GeneratedValue, intPtrType), // i8* functionPtr })); }) .Concat(Enumerable.Repeat(LLVM.ConstNull(imtEntryType), 1)).ToArray()); // Append { 0, 0 } terminator var imtEntryGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(imtEntries), @class.Type.TypeReference.MangledName() + ".imt"); LLVM.SetInitializer(imtEntryGlobal, imtEntries); // Add 1 to differentiate between single entry and IMT array return(LLVM.ConstIntToPtr( LLVM.ConstAdd( LLVM.ConstPtrToInt(imtEntryGlobal, nativeIntType), LLVM.ConstInt(nativeIntType, 1, false)), intPtrType)); } }).ToArray()); // Build list of super types var superTypes = new List <Class>(@class.Depth); var currentClass = @class; while (currentClass != null) { superTypes.Add(currentClass); currentClass = currentClass.BaseType; } // Reverse so that the list start with most inherited object // (allows faster type checking since a given type will always be at a given index) superTypes.Reverse(); // Build super types // Helpful for fast is/as checks on class hierarchy var superTypeCount = LLVM.ConstInt(int32Type, (ulong)@class.Depth + 1, false); var interfacesCount = LLVM.ConstInt(int32Type, (ulong)@class.Interfaces.Count, false); var zero = LLVM.ConstInt(int32Type, 0, false); // Super types global var superTypesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrType, (uint)superTypes.Count), @class.Type.TypeReference.MangledName() + ".supertypes"); var superTypesGlobal = LLVM.ConstInBoundsGEP(superTypesConstantGlobal, new[] { zero, zero }); // Interface map global var interfacesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrType, (uint)@class.Interfaces.Count), @class.Type.TypeReference.MangledName() + ".interfaces"); var interfacesGlobal = LLVM.ConstInBoundsGEP(interfacesConstantGlobal, new[] { zero, zero }); // Build VTable var vtableConstant = LLVM.ConstStructInContext(context, @class.VirtualTable.Select(x => x.GeneratedValue).ToArray(), false); // Build RTTI var runtimeTypeInfoGlobal = @class.GeneratedRuntimeTypeInfoGlobal; var runtimeTypeInfoType = LLVM.GetElementType(LLVM.TypeOf(runtimeTypeInfoGlobal)); var runtimeTypeInfoTypeElements = new TypeRef[LLVM.CountStructElementTypes(runtimeTypeInfoType)]; LLVM.GetStructElementTypes(runtimeTypeInfoType, runtimeTypeInfoTypeElements); var runtimeTypeInfoConstant = LLVM.ConstNamedStruct(runtimeTypeInfoType, new[] { @class.BaseType != null ? @class.BaseType.GeneratedRuntimeTypeInfoGlobal : LLVM.ConstPointerNull(intPtrType), superTypeCount, interfacesCount, superTypesGlobal, interfacesGlobal, LLVM.ConstInt(LLVM.Int1TypeInContext(context), 0, false), // Class initialized? interfaceMethodTableConstant, vtableConstant, LLVM.ConstNull(runtimeTypeInfoTypeElements[(int)RuntimeTypeInfoFields.StaticFields]), }); LLVM.SetInitializer(runtimeTypeInfoGlobal, runtimeTypeInfoConstant); // Build super type list (after RTTI since we need pointer to RTTI) var superTypesConstant = LLVM.ConstArray(intPtrType, superTypes.Select(superType => LLVM.ConstPointerCast(superType.GeneratedRuntimeTypeInfoGlobal, intPtrType)) .ToArray()); LLVM.SetInitializer(superTypesConstantGlobal, superTypesConstant); // Build interface map var interfacesConstant = LLVM.ConstArray(intPtrType, @class.Interfaces.Select( @interface => LLVM.ConstPointerCast(@interface.GeneratedRuntimeTypeInfoGlobal, intPtrType)).ToArray()); LLVM.SetInitializer(interfacesConstantGlobal, interfacesConstant); // Mark RTTI as external LLVM.SetLinkage(runtimeTypeInfoGlobal, Linkage.ExternalLinkage); }
public static void WriteOffsetReference(ITextOutput writer, Instruction instruction) { writer.WriteReference(CecilExtensions.OffsetToString(instruction.Offset), instruction); }
private void BuildRuntimeType(Class @class) { if (@class.IsEmitted) { return; } //Console.WriteLine("Build type {0}", @class); @class.IsEmitted = true; var zero = LLVM.ConstInt(int32LLVM, 0, false); bool isConcreteType = IsConcreteType(@class.Type); // Prepare metadata info var sharpLangModule = TestMode ? LLVM.ConstNull(intPtrLLVM) : metadataPerModule[@class.Type.TypeDefinitionCecil.Module]; var extraTypeInfo = LLVM.ConstNull(intPtrLLVM); var elementTypeSize = zero; // Build RTTI var runtimeTypeInfoGlobal = @class.GeneratedEETypeTokenLLVM; var runtimeTypeInfoType = LLVM.GetElementType(LLVM.TypeOf(runtimeTypeInfoGlobal)); var runtimeTypeInfoTypeElements = new TypeRef[LLVM.CountStructElementTypes(runtimeTypeInfoType)]; LLVM.GetStructElementTypes(runtimeTypeInfoType, runtimeTypeInfoTypeElements); var typeSpecification = @class.Type.TypeReferenceCecil as TypeSpecification; if (typeSpecification != null) { if (typeSpecification is ArrayType || typeSpecification is ByReferenceType || typeSpecification is PointerType) { // Get element type var elementType = GetType(typeSpecification.ElementType, TypeState.VTableEmitted); elementTypeSize = LLVM.ConstIntCast(LLVM.SizeOf(elementType.DefaultTypeLLVM), int32LLVM, false); extraTypeInfo = LLVM.ConstPtrToInt(elementType.Class.GeneratedEETypeRuntimeLLVM, nativeIntLLVM); if (typeSpecification is ArrayType) { extraTypeInfo = LLVM.ConstAdd(extraTypeInfo, LLVM.ConstInt(nativeIntLLVM, (int)ExtraTypeKind.Array, false)); } else if (typeSpecification is ByReferenceType) { extraTypeInfo = LLVM.ConstAdd(extraTypeInfo, LLVM.ConstInt(nativeIntLLVM, (int)ExtraTypeKind.ByRef, false)); } else if (typeSpecification is PointerType) { extraTypeInfo = LLVM.ConstAdd(extraTypeInfo, LLVM.ConstInt(nativeIntLLVM, (int)ExtraTypeKind.Pointer, false)); } extraTypeInfo = LLVM.ConstIntToPtr(extraTypeInfo, intPtrLLVM); } var genericInstanceType = typeSpecification as GenericInstanceType; if (genericInstanceType != null) { // Build global with array of VTable (one for each generic argument) var genericArgumentsTypes = genericInstanceType.GenericArguments.Select(x => GetType(x, TypeState.VTableEmitted)).ToArray(); var genericArgumentesTypeGlobalType = LLVM.ArrayType(intPtrLLVM, (uint)genericArgumentsTypes.Length + 1); var genericArgumentsTypesGlobal = LLVM.AddGlobal(module, genericArgumentesTypeGlobalType, @class.Type.TypeReferenceCecil.MangledName() + ".genericarguments"); LLVM.SetLinkage(genericArgumentsTypesGlobal, Linkage.PrivateLinkage); LLVM.SetInitializer(genericArgumentsTypesGlobal, LLVM.ConstArray(intPtrLLVM, genericArgumentsTypes .Select(x => LLVM.ConstPointerCast(x.Class.GeneratedEETypeTokenLLVM, intPtrLLVM)) .Concat(new[] { LLVM.ConstPointerNull(intPtrLLVM) }) .ToArray())); extraTypeInfo = LLVM.ConstInBoundsGEP(genericArgumentsTypesGlobal, new[] { zero, zero }); extraTypeInfo = LLVM.ConstPointerCast(extraTypeInfo, intPtrLLVM); } } var runtimeTypeFields = new List <ValueRef> { @class.BaseType != null ? @class.BaseType.GeneratedEETypeTokenLLVM : LLVM.ConstPointerNull(intPtrLLVM), LLVM.ConstInt(LLVM.Int8TypeInContext(context), isConcreteType ? 1U : 0U, false), LLVM.ConstNamedStruct(typeDefLLVM, new[] { sharpLangModule, LLVM.ConstInt(int32LLVM, @class.Type.TypeDefinitionCecil.MetadataToken.ToUInt32(), false), }), extraTypeInfo, LLVM.ConstNull(sharpLangTypeType.DefaultTypeLLVM), }; // Prepare the runtime type object var mangledTypeName = Regex.Replace(@class.Type.TypeReferenceCecil.MangledName() + ".sharplangtype", @"(\W)", "_"); var sharpLangTypeGlobal = LLVM.AddGlobal(module, sharpLangTypeType.ObjectTypeLLVM, mangledTypeName); LLVM.SetLinkage(sharpLangTypeGlobal, Linkage.PrivateLinkage); LLVM.SetInitializer(sharpLangTypeGlobal, LLVM.ConstNull(sharpLangTypeType.ObjectTypeLLVM)); if (isConcreteType) { // Build IMT var interfaceMethodTable = new LinkedList <InterfaceMethodTableEntry> [InterfaceMethodTableSize]; foreach (var @interface in @class.Interfaces) { foreach (var interfaceMethod in @interface.Type.TypeReferenceCecil.Resolve().Methods) { var resolvedInterfaceMethod = ResolveGenericMethod(@interface.Type.TypeReferenceCecil, interfaceMethod); // If method is not fully resolved (generic method in interface), ignore it // We are waiting for actual closed uses. if (ResolveGenericsVisitor.ContainsGenericParameters(resolvedInterfaceMethod)) { continue; } var resolvedFunction = CecilExtensions.TryMatchMethod(@class, resolvedInterfaceMethod); if (resolvedFunction == null && @class.Type.TypeReferenceCecil is ArrayType) { var arrayType = corlib.MainModule.GetType(typeof(Array).FullName); var matchingMethod = (MethodReference)arrayType.Methods.First(x => x.Name.StartsWith("InternalArray_") && x.Name.EndsWith(resolvedInterfaceMethod.Name)); if (matchingMethod != null) { if (matchingMethod.HasGenericParameters) { matchingMethod = matchingMethod.MakeGenericMethod(((ArrayType)@class.Type.TypeReferenceCecil).ElementType); } resolvedFunction = GetFunction(matchingMethod); // Manually emit Array functions locally (until proper mscorlib + generic instantiation exists). EmitFunction(resolvedFunction); LLVM.SetLinkage(resolvedFunction.GeneratedValue, Linkage.LinkOnceAnyLinkage); } } if (resolvedFunction == null) { throw new InvalidOperationException(string.Format("Could not find matching method for {0} in {1}", resolvedInterfaceMethod, @class)); } var isInterface = resolvedFunction.DeclaringType.TypeReferenceCecil.Resolve().IsInterface; if (!isInterface && resolvedFunction.MethodReference.Resolve().IsVirtual&& resolvedFunction.VirtualSlot != -1) { // We might have found a base virtual method matching this interface method. // Let's get the actual method override for this virtual slot. resolvedFunction = @class.VirtualTable[resolvedFunction.VirtualSlot]; } // If method is not found, it could be due to covariance/contravariance if (resolvedFunction == null) { throw new InvalidOperationException("Interface method not found"); } var methodId = GetMethodId(resolvedInterfaceMethod); var imtSlotIndex = (int)(methodId % InterfaceMethodTableSize); var imtSlot = interfaceMethodTable[imtSlotIndex]; if (imtSlot == null) { interfaceMethodTable[imtSlotIndex] = imtSlot = new LinkedList <InterfaceMethodTableEntry>(); } imtSlot.AddLast(new InterfaceMethodTableEntry { Function = resolvedFunction, MethodId = GetFunction(resolvedInterfaceMethod).GeneratedValue, // Should be a fake global, that we use as IMT key }); } } var interfaceMethodTableConstant = LLVM.ConstArray(intPtrLLVM, interfaceMethodTable.Select(imtSlot => { if (imtSlot == null) { // No entries: null slot return(LLVM.ConstNull(intPtrLLVM)); } if (imtSlot.Count == 1) { // Single entry var imtEntry = imtSlot.First.Value; return(LLVM.ConstPointerCast(GetVirtualMethod(imtEntry.Function), intPtrLLVM)); } else { // Multiple entries, create IMT array with all entries // TODO: Support covariance/contravariance? var imtEntries = LLVM.ConstArray(imtEntryLLVM, imtSlot.Select(imtEntry => { return(LLVM.ConstNamedStruct(imtEntryLLVM, new[] { imtEntry.MethodId, // i8* functionId LLVM.ConstPointerCast(GetVirtualMethod(imtEntry.Function), intPtrLLVM), // i8* functionPtr })); }) .Concat(Enumerable.Repeat(LLVM.ConstNull(imtEntryLLVM), 1)).ToArray()); // Append { 0, 0 } terminator var imtEntryGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(imtEntries), @class.Type.TypeReferenceCecil.MangledName() + ".imt"); LLVM.SetLinkage(imtEntryGlobal, Linkage.PrivateLinkage); LLVM.SetInitializer(imtEntryGlobal, imtEntries); // Add 1 to differentiate between single entry and IMT array return(LLVM.ConstIntToPtr( LLVM.ConstAdd( LLVM.ConstPtrToInt(imtEntryGlobal, nativeIntLLVM), LLVM.ConstInt(nativeIntLLVM, 1, false)), intPtrLLVM)); } }).ToArray()); // Build list of super types var superTypes = new List <Class>(@class.Depth); var currentClass = @class; while (currentClass != null) { superTypes.Add(currentClass); currentClass = currentClass.BaseType; } // Reverse so that the list start with most inherited object // (allows faster type checking since a given type will always be at a given index) superTypes.Reverse(); // Build super types // Helpful for fast is/as checks on class hierarchy var superTypeCount = LLVM.ConstInt(int32LLVM, (ulong)@class.Depth + 1, false); var interfacesCount = LLVM.ConstInt(int32LLVM, (ulong)@class.Interfaces.Count, false); // Super types global var superTypesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrLLVM, (uint)superTypes.Count), @class.Type.TypeReferenceCecil.MangledName() + ".supertypes"); LLVM.SetLinkage(superTypesConstantGlobal, Linkage.PrivateLinkage); var superTypesGlobal = LLVM.ConstInBoundsGEP(superTypesConstantGlobal, new[] { zero, zero }); // Interface map global var interfacesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrLLVM, (uint)@class.Interfaces.Count), @class.Type.TypeReferenceCecil.MangledName() + ".interfaces"); LLVM.SetLinkage(interfacesConstantGlobal, Linkage.PrivateLinkage); var interfacesGlobal = LLVM.ConstInBoundsGEP(interfacesConstantGlobal, new[] { zero, zero }); // Build VTable var vtableConstant = LLVM.ConstNamedStruct(@class.VTableTypeLLVM, @class.VirtualTable.Select(x => GetVirtualMethod(x)).ToArray()); var staticFieldsInitializer = LLVM.ConstNamedStruct(runtimeTypeInfoTypeElements[(int)RuntimeTypeInfoFields.StaticFields], @class.StaticFields.Select(field => { var fieldType = field.Value.Type; if ((field.Key.Attributes & FieldAttributes.HasFieldRVA) != 0) { var initialValue = field.Key.InitialValue; // Seems like if type size is 8, it uses int64 as backing type // Maybe at some point it might be better to encode static fields in a big byte array and use casts instead? if (LLVM.GetTypeKind(fieldType.DefaultTypeLLVM) == TypeKind.IntegerTypeKind) { unsafe { fixed(byte *initalValueStart = initialValue) { if (LLVM.GetIntTypeWidth(fieldType.DefaultTypeLLVM) == 64) { return(LLVM.ConstInt(fieldType.DefaultTypeLLVM, *(ulong *)initalValueStart, false)); } if (LLVM.GetIntTypeWidth(fieldType.DefaultTypeLLVM) == 32) { return(LLVM.ConstInt(fieldType.DefaultTypeLLVM, *(uint *)initalValueStart, false)); } } } } // Otherwise, for now we assume that if there was a RVA, it was a type with custom layout (default type is i8[]), // as currently generated by compiler in <PrivateImplementationDetails> class. if (LLVM.GetTypeKind(fieldType.DefaultTypeLLVM) != TypeKind.ArrayTypeKind) { throw new NotSupportedException(); } var arrayElementType = LLVM.Int8TypeInContext(context); return(LLVM.ConstArray(arrayElementType, initialValue.Select(x => LLVM.ConstInt(arrayElementType, x, false)).ToArray())); } return(LLVM.ConstNull(fieldType.DefaultTypeLLVM)); }).ToArray()); runtimeTypeFields.AddRange(new[] { superTypeCount, interfacesCount, superTypesGlobal, interfacesGlobal, LLVM.ConstInt(LLVM.Int8TypeInContext(context), 0, false), // Class initialized? LLVM.ConstIntCast(LLVM.SizeOf(@class.Type.ObjectTypeLLVM), int32LLVM, false), elementTypeSize, interfaceMethodTableConstant, LLVM.ConstInt(int32LLVM, (ulong)@class.VirtualTable.Count, false), vtableConstant, staticFieldsInitializer, }); var runtimeTypeInfoConstant = LLVM.ConstNamedStruct(runtimeTypeInfoType, runtimeTypeFields.ToArray()); LLVM.SetInitializer(runtimeTypeInfoGlobal, runtimeTypeInfoConstant); // Build super type list (after RTTI since we need pointer to RTTI) var superTypesConstant = LLVM.ConstArray(intPtrLLVM, superTypes.Select(superType => LLVM.ConstPointerCast(superType.GeneratedEETypeTokenLLVM, intPtrLLVM)) .ToArray()); LLVM.SetInitializer(superTypesConstantGlobal, superTypesConstant); // Build interface map var interfacesConstant = LLVM.ConstArray(intPtrLLVM, @class.Interfaces.Select( @interface => LLVM.ConstPointerCast(@interface.GeneratedEETypeTokenLLVM, intPtrLLVM)).ToArray()); LLVM.SetInitializer(interfacesConstantGlobal, interfacesConstant); } else { // Non-concrete type, create type with current fields var runtimeTypeInfoConstant = LLVM.ConstNamedStruct(runtimeTypeInfoType, runtimeTypeFields.ToArray()); LLVM.SetInitializer(runtimeTypeInfoGlobal, runtimeTypeInfoConstant); } // Mark RTTI as external LLVM.SetLinkage(runtimeTypeInfoGlobal, Linkage.ExternalLinkage); }
// MethodReference ObjectDisposedExceptionRef; // MethodReference ExceptionRef; // enum OnFinalize // { // Nothing, // Dispose, // Custom, // } public override void Execute() { var onFinalizeAttr = Config.Attributes().FirstOrDefault(x => x.Name == "OnFinalize").Value.ToString(); // OnFinalize onFinalize = OnFinalize.Nothing; // MethodReference customMethod = null; MethodReference finalizer = null; if (!string.IsNullOrWhiteSpace(onFinalizeAttr))// && onFinalizeAttr.Contains('.')) { try { finalizer = ModuleDefinition .GetTypes() .First(x => x.IsClass && x.FullName == Path.GetFileNameWithoutExtension(onFinalizeAttr) ) .Methods .First(x => x.IsStatic && x.IsMatch( Path.GetExtension(onFinalizeAttr).TrimStart('.'), "IDisposable", ModuleDefinition.TypeSystem.Boolean.Name ) ); } catch {} if (finalizer == null) { LogError($"Could not find static method '{onFinalizeAttr}(IDisposable, bool)'"); return; } else { LogInfo($"Finalizer for disposable objects registered '{finalizer.FullName}'"); } } /*switch (onFinalizeAttr?.Value.ToString()) * { * // case "Nothing": * // onFinalize = OnFinalize.Nothing; * // break; * case "Dispose": * onFinalize = OnFinalize.Dispose; * break; * case "Custom": * onFinalize = OnFinalize.Custom; * customMethod = ModuleDefinition * .GetTypes() * .Where(x => * x.IsClass && * !x.IsGeneratedCode() && * x.Methods.Any(x => * x.IsStatic && * x.CustomAttributes.ContainsOnFinalize() && * x.IsMatch(ModuleDefinition.TypeSystem.Object.Name, ModuleDefinition.TypeSystem.Boolean.Name) * ) * ); * if (customMethod == null) * { * LogError("Could not find static method with attribute 'DisposeGuard.Fody.OnFinalize'"); * return; * } * break; * // default: * // onFinalize = OnFinalize.Nothing; * // break; * // LogWarning("You must specify 'OnFinalize' parameter."); * // return; * }*/ var objectDisposedExceptionCtor = ModuleDefinition.ImportReference( FindType("System.ObjectDisposedException").Find(".ctor", "String") ); var exceptionCtor = ModuleDefinition.ImportReference( FindType("System.Exception").Find(".ctor", "String") ); foreach (var type in ModuleDefinition .GetTypes() .Where(x => x.IsClass && // !x.IsAbstract && !x.IsGeneratedCode() //&& // !x.CustomAttributes.ContainsDoNotTrack() ) ) { // if (!type.Interfaces.Any(x => x.InterfaceType.FullName == "System.IDisposable")) // { // continue; // } var disposeMethod = type.Methods .FirstOrDefault(x => !x.IsStatic && !x.HasParameters && (x.Name == "Dispose" || x.Name == "System.IDisposable.Dispose") ); if (disposeMethod == null) { // LogInfo($"Cannot find dispose method in class {type.FullName}"); continue; } if (!isIDisposable(type)) { LogWarning($"Class '{type.FullName}' contains 'Dispose' method but not implements 'IDisposable' interface"); continue; } LogInfo($"Patching class '{type.FullName}'"); var disposedField = createDisposedField(); type.Fields.Add(disposedField); ////// MODIFY DISPOSE begin ////// { // var validSequencePoint = disposeMethod.DebugInformation.SequencePoints.FirstOrDefault(); disposeMethod.Body.SimplifyMacros(); disposeMethod.Body.Instructions.InsertAtStart( Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Ldc_I4_1), Instruction.Create(OpCodes.Stfld, disposedField) ); // if (validSequencePoint != null) // CecilExtensions.HideLineFromDebugger(validSequencePoint); disposeMethod.Body.OptimizeMacros(); } ////// MODIFY DISPOSE end ////// var throwIfDisposedMethod = createThrowIfDisposedMethod(disposedField, type.FullName); type.Methods.Add(throwIfDisposedMethod); ////// ADD GUARDS begin ////// foreach (var method in type.Methods) { if (method.Name == ".ctor") { continue; } if (method.IsMatch("Finalize")) { continue; } if (method.IsStatic) { continue; } if (method.Name == "Dispose") { continue; } if (method.Name == "IsDisposed") { continue; } if (method.Name == throwIfDisposedMethod.Name) { continue; } // if (!method.HasBody) // continue; if (method.IsPrivate) { continue; } var validSequencePoint = method.DebugInformation.SequencePoints.FirstOrDefault(); method.Body.SimplifyMacros(); method.Body.Instructions.InsertAtStart( Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Call, throwIfDisposedMethod) ); if (validSequencePoint != null) { CecilExtensions.HideLineFromDebugger(validSequencePoint); } method.Body.OptimizeMacros(); } ////// ADD GUARDS end ////// ////// MODIFY FINALIZER begin ////// MethodDefinition _finalizeMethod() { var finalizeMethod = type.Methods.FirstOrDefault(x => !x.IsStatic && x.IsMatch("Finalize")); if (finalizeMethod == null) { finalizeMethod = new MethodDefinition( "Finalize", MethodAttributes.HideBySig | MethodAttributes.Family | MethodAttributes.Virtual, ModuleDefinition.TypeSystem.Void ); finalizeMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); type.Methods.Add(finalizeMethod); } return(finalizeMethod); } if (finalizer != null) { var method = _finalizeMethod(); method.Body.SimplifyMacros(); method.Body.Instructions.InsertAtStart( Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Ldfld, disposedField), Instruction.Create(OpCodes.Call, finalizer) ); method.Body.OptimizeMacros(); } /*switch (onFinalize) * { * case OnFinalize.Dispose: * { * var method = _finalizeMethod(); * method.Body.SimplifyMacros(); * method.Body.Instructions.InsertAtStart( * Instruction.Create(OpCodes.Ldarg_0), * Instruction.Create(OpCodes.Call, disposeMethod) * ); * method.Body.OptimizeMacros(); * } * break; * case OnFinalize.Custom: * { * var method = _finalizeMethod(); * method.Body.SimplifyMacros(); * method.Body.Instructions.InsertAtStart( * Instruction.Create(OpCodes.Ldarg_0), * Instruction.Create(OpCodes.Ldarg_0), * Instruction.Create(OpCodes.Ldfld, disposedField), * Instruction.Create(OpCodes.Call, customMethod) * ); * method.Body.OptimizeMacros(); * } * break; * }*/ ////// MODIFY FINALIZER end ////// // var disposeMethod = disposeMethods.FirstOrDefault(x => !x.HasParameters); // if (disposeMethod == null) // { // // If the base type is not in the same assembly as the type we're processing // // then we want to patch the Dispose method. If it is in the same // // assembly then the patch code gets added to the Dispose method of the // // base class, so we skip this type. // if (type.BaseType.Scope == type.Scope) // continue; // disposeMethod = disposeMethods[0]; // } // ProcessDisposeMethod(disposeMethod); // var constructors = type.Methods.Where(x => !x.IsStatic && x.IsConstructor).ToList(); // if (constructors.Count != 0) // { // foreach (var ctor in constructors) // { // ProcessConstructor(ctor); // } // } } // CleanReferences(); FieldDefinition createDisposedField() { return(new FieldDefinition( "__disposed__" + suffix, FieldAttributes.Private | FieldAttributes.HasFieldRVA, ModuleDefinition.TypeSystem.Boolean )); } MethodDefinition createThrowIfDisposedMethod(FieldDefinition disposedField, string className) { var method = new MethodDefinition( "__ThrowIfDisposed__" + suffix, MethodAttributes.Private | MethodAttributes.HideBySig, ModuleDefinition.TypeSystem.Void ); var returnInstruction = Instruction.Create(OpCodes.Ret); method.Body.Instructions.InsertAtStart( Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Ldfld, disposedField), Instruction.Create(OpCodes.Brfalse_S, returnInstruction), Instruction.Create(OpCodes.Ldstr, className), Instruction.Create(OpCodes.Newobj, objectDisposedExceptionCtor), Instruction.Create(OpCodes.Throw), returnInstruction ); return(method); } bool isIDisposable(TypeDefinition type) { if (type.Interfaces.Any(i => i.InterfaceType.FullName.Equals("System.IDisposable"))) { return(true); } if (type.FullName.Equals("System.IDisposable")) { return(true); } return(type.BaseType != null && isIDisposable(type.BaseType.Resolve())); } }
/// <summary> /// Compiles the specified class. /// </summary> /// <param name="typeReference">The type definition.</param> /// <returns></returns> private Class CreateClass(TypeReference typeReference) { Class @class; if (classes.TryGetValue(typeReference, out @class)) { return(@class); } bool processFields = false; switch (typeReference.MetadataType) { case MetadataType.Void: case MetadataType.Boolean: case MetadataType.Char: case MetadataType.Byte: case MetadataType.SByte: case MetadataType.Int16: case MetadataType.UInt16: case MetadataType.Int32: case MetadataType.UInt32: case MetadataType.Int64: case MetadataType.UInt64: case MetadataType.IntPtr: case MetadataType.UIntPtr: case MetadataType.Single: case MetadataType.Double: case MetadataType.String: { break; } case MetadataType.ValueType: case MetadataType.Class: case MetadataType.Object: case MetadataType.GenericInstance: { // Process non-static fields processFields = true; break; } default: throw new NotImplementedException(); } var type = GetType(typeReference); // Create class version (boxed version with VTable) var boxedType = type.ObjectType; var dataType = type.DataType; var stackType = type.StackType; @class = new Class(type, typeReference, dataType, boxedType, stackType); classes.Add(typeReference, @class); if (processFields) { var typeDefinition = typeReference.Resolve(); var fieldTypes = new List <TypeRef>(typeDefinition.Fields.Count); var parentClass = typeDefinition.BaseType != null?GetClass(ResolveGenericsVisitor.Process(typeReference, typeDefinition.BaseType)) : null; // Add parent class if (parentClass != null) { @class.BaseType = parentClass; // Add parent classes @class.VirtualTable.AddRange(parentClass.VirtualTable); foreach (var @interface in parentClass.Interfaces) { @class.Interfaces.Add(@interface); } } // Build methods slots // TODO: This will trigger their compilation, but maybe we might want to defer that later // (esp. since vtable is not built yet => recursion issues) CompileClassMethods(@class); if (typeDefinition.IsInterface) { // Interface: No need for vtable, we can just use object's one var vtableGlobal = GetClass(assembly.MainModule.Import(typeof(object))).GeneratedRuntimeTypeInfoGlobal; LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(vtableGlobal), dataType }, false); @class.GeneratedRuntimeTypeInfoGlobal = vtableGlobal; } else { // Get parent type RTTI var runtimeTypeInfoType = LLVM.PointerType(LLVM.Int8TypeInContext(context), 0); var parentRuntimeTypeInfo = parentClass != null ? LLVM.ConstPointerCast(parentClass.GeneratedRuntimeTypeInfoGlobal, runtimeTypeInfoType) : LLVM.ConstPointerNull(runtimeTypeInfoType); // Build vtable var vtableConstant = LLVM.ConstStructInContext(context, @class.VirtualTable.Select(x => x.GeneratedValue).ToArray(), false); // Build IMT var interfaceMethodTable = new LinkedList <InterfaceMethodTableEntry> [InterfaceMethodTableSize]; foreach (var @interface in typeDefinition.Interfaces) { var resolvedInterface = ResolveGenericsVisitor.Process(typeReference, @interface); @class.Interfaces.Add(GetClass(resolvedInterface)); // TODO: Add any inherited interface inherited by the resolvedInterface as well } foreach (var @interface in @class.Interfaces) { foreach (var interfaceMethod in @interface.TypeReference.Resolve().Methods) { var resolvedInterfaceMethod = ResolveGenericMethod(@interface.TypeReference, interfaceMethod); var resolvedFunction = CecilExtensions.TryMatchMethod(@class, resolvedInterfaceMethod); // If method is not found, it could be due to covariance/contravariance if (resolvedFunction == null) { throw new InvalidOperationException("Interface method not found"); } var methodId = GetMethodId(resolvedInterfaceMethod); var imtSlotIndex = (int)(methodId % interfaceMethodTable.Length); var imtSlot = interfaceMethodTable[imtSlotIndex]; if (imtSlot == null) { interfaceMethodTable[imtSlotIndex] = imtSlot = new LinkedList <InterfaceMethodTableEntry>(); } imtSlot.AddLast(new InterfaceMethodTableEntry { Function = resolvedFunction, MethodId = methodId, SlotIndex = imtSlotIndex }); } } var interfaceMethodTableConstant = LLVM.ConstArray(imtEntryType, interfaceMethodTable.Select(imtSlot => { if (imtSlot == null) { // No entries: null slot return(LLVM.ConstNull(imtEntryType)); } if (imtSlot.Count > 1) { throw new NotImplementedException("IMT with more than one entry per slot is not implemented yet."); } var imtEntry = imtSlot.First.Value; return(LLVM.ConstNamedStruct(imtEntryType, new[] { LLVM.ConstPointerCast(imtEntry.Function.GeneratedValue, intPtrType), // i8* functionPtr LLVM.ConstInt(int32Type, (ulong)imtEntry.MethodId, false), // i32 functionId LLVM.ConstPointerNull(LLVM.PointerType(imtEntryType, 0)), // IMTEntry* nextSlot })); }).ToArray()); // Build static fields foreach (var field in typeDefinition.Fields) { if (!field.IsStatic) { continue; } var fieldType = CreateType(assembly.MainModule.Import(ResolveGenericsVisitor.Process(typeReference, field.FieldType))); @class.Fields.Add(field, new Field(field, @class, fieldType, fieldTypes.Count)); fieldTypes.Add(fieldType.DefaultType); } var staticFieldsEmpty = LLVM.ConstNull(LLVM.StructTypeInContext(context, fieldTypes.ToArray(), false)); fieldTypes.Clear(); // Reused for non-static fields after // Build RTTI var runtimeTypeInfoConstant = LLVM.ConstStructInContext(context, new[] { parentRuntimeTypeInfo, interfaceMethodTableConstant, vtableConstant, staticFieldsEmpty }, false); var vtableGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(runtimeTypeInfoConstant), string.Empty); LLVM.SetInitializer(vtableGlobal, runtimeTypeInfoConstant); LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(vtableGlobal), dataType }, false); @class.GeneratedRuntimeTypeInfoGlobal = vtableGlobal; } // Build actual type data (fields) // Add fields and vtable slots from parent class if (parentClass != null && typeReference.MetadataType == MetadataType.Class) { fieldTypes.Add(parentClass.DataType); } foreach (var field in typeDefinition.Fields) { if (field.IsStatic) { continue; } var fieldType = CreateType(assembly.MainModule.Import(ResolveGenericsVisitor.Process(typeReference, field.FieldType))); @class.Fields.Add(field, new Field(field, @class, fieldType, fieldTypes.Count)); fieldTypes.Add(fieldType.DefaultType); } LLVM.StructSetBody(dataType, fieldTypes.ToArray(), false); } return(@class); }
static FieldDefinition GetFieldDefinition(FieldReference field) { return(CecilExtensions.ResolveWithinSameModule(field)); }
public override void Execute() { // Debugger.Launch(); var soMeta = ModuleDefinition.FindAssembly("Someta"); CecilExtensions.LogInfo = LogInfo; CecilExtensions.LogWarning = LogWarning; CecilExtensions.LogError = LogError; CecilExtensions.Initialize(ModuleDefinition, TypeSystem, soMeta); var extensionPointInterface = ModuleDefinition.FindType("Someta", "IExtensionPoint", soMeta); var stateExtensionPointInterface = ModuleDefinition.FindType("Someta", "IStateExtensionPoint`1", soMeta, "T"); var stateExtensionPointInterfaceBase = ModuleDefinition.FindType("Someta", "IStateExtensionPoint", soMeta); var propertyGetInterceptorInterface = ModuleDefinition.FindType("Someta", "IPropertyGetInterceptor", soMeta); var propertySetInterceptorInterface = ModuleDefinition.FindType("Someta", "IPropertySetInterceptor", soMeta); var eventAddInterceptorInterface = ModuleDefinition.FindType("Someta", "IEventAddInterceptor", soMeta); var eventRemoveInterceptorInterface = ModuleDefinition.FindType("Someta", "IEventRemoveInterceptor", soMeta); var methodInterceptorInterface = ModuleDefinition.FindType("Someta", "IMethodInterceptor", soMeta); var asyncMethodInterceptorInterface = ModuleDefinition.FindType("Someta", "IAsyncMethodInterceptor", soMeta); var nonPublicAccessInterface = ModuleDefinition.FindType("Someta", "INonPublicAccess", soMeta); var asyncInvoker = ModuleDefinition.FindType("Someta.Helpers", "AsyncInvoker", soMeta); var asyncInvokerWrap = ModuleDefinition.FindMethod(asyncInvoker, "Wrap"); var asyncInvokerUnwrap = ModuleDefinition.FindMethod(asyncInvoker, "Unwrap"); var instanceInitializerInterfaceBase = ModuleDefinition.FindType("Someta", "IInstanceInitializer", soMeta); var instanceInitializerInterface = ModuleDefinition.FindType("Someta", "IInstanceInitializer`1", soMeta, "T"); // var interceptorScopeAttribute = ModuleDefinition.FindType("Someta", "InterceptorScopeAttribute", soMeta); // var requireScopeInterceptorInterface = ModuleDefinition.FindType("Someta", "IRequireScopeInterceptor", soMeta); var extensionPointScopesClass = ModuleDefinition.FindType("Someta", "ExtensionPointScopes", soMeta); var extensionPointScopesClassDefinition = extensionPointScopesClass.Resolve(); // var interceptorScopeInterface = interceptorScopesClassDefinition.NestedTypes.Single(x => x.Name == "Scope"); var extensionPointScopePropertyInterface = extensionPointScopesClassDefinition.NestedTypes.Single(x => x.Name == "Property"); var extensionPointScopeMethodInterface = extensionPointScopesClassDefinition.NestedTypes.Single(x => x.Name == "Method"); var extensionPointScopeClassInterface = extensionPointScopesClassDefinition.NestedTypes.Single(x => x.Name == "Class"); var propertyGetInterceptions = new List <(PropertyDefinition, ExtensionPointAttribute)>(); var propertySetInterceptions = new List <(PropertyDefinition, ExtensionPointAttribute)>(); var eventAddInterceptions = new List <(EventDefinition, ExtensionPointAttribute)>(); var eventRemoveInterceptions = new List <(EventDefinition, ExtensionPointAttribute)>(); var methodInterceptions = new List <(MethodDefinition, ExtensionPointAttribute)>(); var asyncMethodInterceptions = new List <(MethodDefinition, ExtensionPointAttribute)>(); var classEnhancers = new List <(TypeDefinition, ExtensionPointAttribute)>(); var stateInterceptions = new List <(IMemberDefinition, ExtensionPointAttribute)>(); var instanceInitializers = new List <(IMemberDefinition, ExtensionPointAttribute)>(); var propertyGetInterceptorWeaver = new PropertyGetInterceptorWeaver(CecilExtensions.Context, propertyGetInterceptorInterface); var propertySetInterceptorWeaver = new PropertySetInterceptorWeaver(CecilExtensions.Context, propertySetInterceptorInterface); var eventInterceptorWeaver = new EventInterceptorWeaver(CecilExtensions.Context); var methodInterceptorWeaver = new MethodInterceptorWeaver(CecilExtensions.Context, methodInterceptorInterface, asyncMethodInterceptorInterface); var asyncMethodInterceptorWeaver = new AsyncMethodInterceptorWeaver(CecilExtensions.Context, asyncMethodInterceptorInterface, asyncInvokerWrap, asyncInvokerUnwrap); var classEnhancerWeaver = new ClassEnhancerWeaver(CecilExtensions.Context); var stateWeaver = new StateWeaver(CecilExtensions.Context); var instanceInitializerWeaver = new InstanceInitializerWeaver(CecilExtensions.Context); // unscopedInterface: If present, and if genericTypes is empty (meaning no specific scope was specified), // unscopedInterface will be checked as a fallback. bool HasScope(ExtensionPointAttribute interceptor, TypeReference interfaceType, ExtensionPointScope scope, TypeReference unscopedInterface = null) { var genericTypes = interceptor.AttributeType.FindGenericInterfaces(interfaceType).ToArray(); foreach (var genericType in genericTypes) { var argument = genericType.GenericArguments[0]; TypeReference scopeInterface = null; switch (scope) { case ExtensionPointScope.Class: scopeInterface = extensionPointScopeClassInterface; break; case ExtensionPointScope.Property: scopeInterface = extensionPointScopePropertyInterface; break; case ExtensionPointScope.Method: scopeInterface = extensionPointScopeMethodInterface; break; } if (argument.CompareTo(scopeInterface)) { return(true); } } // If no scope was specified, we consider the scope satisfied if an unscoped version is satisfied // Furthermore, the scope must match the member type. var isScopeMatchedWithMember = interceptor.Scope == scope; return(unscopedInterface != null && genericTypes.Length == 0 && unscopedInterface.IsAssignableFrom(interceptor.AttributeType) && isScopeMatchedWithMember); } // Inventory candidate classes var allTypes = ModuleDefinition.GetAllTypes(); var assemblyInterceptorAttributes = ModuleDefinition.Assembly .GetCustomAttributesIncludingSubtypes(extensionPointInterface) .ToArray(); ExtensionPointAttribute[] assemblyInterceptors; // If we have any assembly-level interceptors, we create a special state class to hold the attribute instances (since attributes // can contain state, but getting attributes through reflection always returns a new instance. if (assemblyInterceptorAttributes.Any()) { // Debugger.Launch(); var assemblyState = new TypeDefinition("Someta", "AssemblyState", TypeAttributes.Public, TypeSystem.ObjectReference); ModuleDefinition.Types.Add(assemblyState); /* * var constructorWithTarget = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, TypeSystem.VoidReference); * constructorWithTarget.Body.Emit(il => * { * il.Emit(OpCodes.Ret); * }); * assemblyState.Methods.Add(constructorWithTarget); */ CecilExtensions.Context.AssemblyState = assemblyState; assemblyInterceptors = assemblyInterceptorAttributes .Select(x => new ExtensionPointAttribute(assemblyState, ModuleDefinition.Assembly, x, ModuleDefinition.Assembly.CustomAttributes.IndexOf(x), ExtensionPointScope.Assembly)) .ToArray(); foreach (var interceptor in assemblyInterceptors) { var fieldName = interceptor.AttributeType.FullName.Replace(".", "$"); var attributeField = new FieldDefinition(fieldName, FieldAttributes.Static | FieldAttributes.Public, interceptor.AttributeType); var index = ModuleDefinition.Assembly.CustomAttributes.IndexOf(interceptor.Attribute); assemblyState.Fields.Add(attributeField); assemblyState.EmitToStaticConstructor(il => { il.EmitGetAssemblyAttributeByIndex(index, interceptor.AttributeType); il.SaveField(attributeField); }); } } else { assemblyInterceptors = new ExtensionPointAttribute[0]; } // var moduleInterceptors = ModuleDefinition // .GetCustomAttributesIncludingSubtypes(extensionPointInterface) // .Select(x => new ExtensionPointAttribute(null, ModuleDefinition, x, ModuleDefinition.CustomAttributes.IndexOf(x), ExtensionPointScope.Module)) // .ToArray(); // var assemblyAndModuleInterceptors = assemblyInterceptors.Concat(moduleInterceptors).ToArray(); foreach (var type in allTypes) { // We can get into recursion scenarios if we allow extension points on extension points. For now, let's naively prohibit this if (extensionPointInterface.IsAssignableFrom(type)) { continue; } var classInterceptors = type .GetCustomAttributesInAncestry(extensionPointInterface) .Select(x => new ExtensionPointAttribute(x.DeclaringType, x.DeclaringType, x.Attribute, x.DeclaringType.CustomAttributes.IndexOf(x.Attribute), ExtensionPointScope.Class)) .Concat(assemblyInterceptors /*.Select(x => new ExtensionPointAttribute(type, x.DeclaringMember, x.Attribute, x.Index, x.Scope))*/) .ToArray(); foreach (var classInterceptor in classInterceptors) { LogInfo($"Found class interceptor {classInterceptor.AttributeType}"); if (nonPublicAccessInterface.IsAssignableFrom(classInterceptor.AttributeType)) { LogInfo($"Discovered class enhancer {classInterceptor.AttributeType.FullName} at {type.FullName}"); classEnhancers.Add((type, classInterceptor)); } if (HasScope(classInterceptor, stateExtensionPointInterface, ExtensionPointScope.Class, stateExtensionPointInterfaceBase)) { LogInfo($"Discovered class state interceptor {classInterceptor.AttributeType.FullName} at {type.FullName}"); stateInterceptions.Add((type, classInterceptor)); } if (HasScope(classInterceptor, instanceInitializerInterface, ExtensionPointScope.Class, instanceInitializerInterfaceBase)) { LogInfo($"Discovered instance initializer {classInterceptor.AttributeType.FullName} at {type.FullName}"); instanceInitializers.Add((type, classInterceptor)); } } foreach (var property in type.Properties) { var interceptors = property.GetCustomAttributesIncludingSubtypes(extensionPointInterface) .Select(x => new ExtensionPointAttribute(type, property, x, property.CustomAttributes.IndexOf(x), ExtensionPointScope.Property)) .Concat(classInterceptors); foreach (var interceptor in interceptors) { if (propertyGetInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered property get interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{property.Name}"); propertyGetInterceptions.Add((property, interceptor)); } if (propertySetInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered property set interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{property.Name}"); propertySetInterceptions.Add((property, interceptor)); } if (HasScope(interceptor, stateExtensionPointInterface, ExtensionPointScope.Property, stateExtensionPointInterfaceBase)) { LogInfo($"Discovered property state interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{property.Name}"); stateInterceptions.Add((property, interceptor)); } if (HasScope(interceptor, instanceInitializerInterface, ExtensionPointScope.Property, instanceInitializerInterfaceBase)) { LogInfo($"Discovered instance initializer {interceptor.AttributeType.FullName} at {type.FullName}"); instanceInitializers.Add((property, interceptor)); } } } foreach (var @event in type.Events) { // Debugger.Launch(); var interceptors = @event.GetCustomAttributesIncludingSubtypes(extensionPointInterface) .Select(x => new ExtensionPointAttribute(type, @event, x, @event.CustomAttributes.IndexOf(x), ExtensionPointScope.Event)) .Concat(classInterceptors); foreach (var interceptor in interceptors) { if (eventAddInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered event add interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{@event.Name}"); eventAddInterceptions.Add((@event, interceptor)); } if (eventRemoveInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered event remove interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{@event.Name}"); eventRemoveInterceptions.Add((@event, interceptor)); } } } foreach (var method in type.Methods.Where(x => !x.IsConstructor)) { var interceptors = method.GetCustomAttributesIncludingSubtypes(extensionPointInterface) .Select(x => new ExtensionPointAttribute(type, method, x, method.CustomAttributes.IndexOf(x), ExtensionPointScope.Method)) .Concat(classInterceptors); foreach (var interceptor in interceptors) { if (methodInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered method interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{method.Name}"); methodInterceptions.Add((method, interceptor)); } if (asyncMethodInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered async method interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{method.Name}"); asyncMethodInterceptions.Add((method, interceptor)); } if (HasScope(interceptor, stateExtensionPointInterface, ExtensionPointScope.Method, stateExtensionPointInterfaceBase)) { LogInfo($"Discovered method state interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{method.Name}"); stateInterceptions.Add((method, interceptor)); } if (HasScope(interceptor, instanceInitializerInterface, ExtensionPointScope.Method, instanceInitializerInterfaceBase)) { LogInfo($"Discovered instance initializer {interceptor.AttributeType.FullName} at {type.FullName}"); instanceInitializers.Add((method, interceptor)); } } } } foreach (var(property, interceptor) in propertyGetInterceptions) { propertyGetInterceptorWeaver.Weave(property, interceptor); } foreach (var(property, interceptor) in propertySetInterceptions) { propertySetInterceptorWeaver.Weave(property, interceptor); } foreach (var(@event, interceptor) in eventAddInterceptions) { eventInterceptorWeaver.Weave(@event, interceptor, isAdd: true); } foreach (var(@event, interceptor) in eventRemoveInterceptions) { eventInterceptorWeaver.Weave(@event, interceptor, isAdd: false); } foreach (var(method, interceptor) in methodInterceptions) { methodInterceptorWeaver.Weave(method, interceptor); } foreach (var(method, interceptor) in asyncMethodInterceptions) { asyncMethodInterceptorWeaver.Weave(method, interceptor); } foreach (var(type, interceptor) in classEnhancers) { classEnhancerWeaver.Weave(type, interceptor); } foreach (var(member, interceptor) in stateInterceptions) { stateWeaver.Weave(member, interceptor); } foreach (var(type, interceptor) in instanceInitializers) { instanceInitializerWeaver.Weave(type, interceptor); } }
static MethodDefinition GetMethodDefinition(MethodReference method) { return(CecilExtensions.ResolveWithinSameModule(method)); }
public DataConverterGenerator(IAssemblyResolver assemblyResolver, AssemblyDefinition assembly) { this.assembly = assembly; // Early exit if Serialization assembly is not referenced. AssemblyDefinition mscorlibAssembly; try { // In case assembly has been modified, // add AssemblyProcessedAttribute to assembly so that it doesn't get processed again mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly); if (mscorlibAssembly == null) { throw new InvalidOperationException("Missing mscorlib.dll from assembly"); } } catch { return; } listType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(List <>).FullName); dictionaryType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Dictionary <,>).FullName); this.dataPropertyTypeVisitor = new DataPropertyTypeVisitor(this); // Process types with DataConverterAttribute foreach (var type in assembly.EnumerateTypes()) { // Generic types are not supported. if (type.HasGenericParameters) { continue; } // Do not allow nested type to be generic (not properly supported, since it could be the enclosing type which is generic) if (!(type.IsPublic || (type.IsNestedPublic && !type.HasGenericParameters))) { continue; } var dataInfo = this.GenerateDataTypeInfo(type); if (dataInfo != null) { var dataConverterInfo = new DataConverterInfo { Generate = (dataInfo.Flags & DataTypeFlags.Generated) != 0, DataInfo = dataInfo, Type = this.GetDataConverterType(type), }; this.processedTypes.Add(type, dataInfo); this.processedConverterTypes.Add(type, dataConverterInfo); } var additionalConverterAttribute = GetAdditionalDataAttribute(type); if (additionalConverterAttribute != null) { var baseTypeArgument = GetAdditionalBaseType(type); var dataConverterInfo = new DataConverterInfo { DataInfo = this.GenerateDataTypeInfo(baseTypeArgument), Type = this.GetDataConverterType(type), }; foreach (var namedArgument in additionalConverterAttribute.Properties) { switch (namedArgument.Name) { case "AutoGenerate": dataConverterInfo.Generate = (bool)namedArgument.Argument.Value; break; } } this.processedConverterTypes.Add(type, dataConverterInfo); } } }