protected void ScanMethod(MethodBase aMethod, bool aIsPlug, string sourceItem) { var xParams = aMethod.GetParameters(); var xParamTypes = new Type[xParams.Length]; // Dont use foreach, enum generaly keeps order but // isn't guaranteed. string xMethodFullName = LabelName.GenerateFullName(aMethod); for (int i = 0; i < xParams.Length; i++) { xParamTypes[i] = xParams[i].ParameterType; Queue(xParamTypes[i], xMethodFullName, "Parameter"); } var xIsDynamicMethod = aMethod.DeclaringType == null; // Queue Types directly related to method if (!aIsPlug) { // Don't queue declaring types of plugs if (!xIsDynamicMethod) { // dont queue declaring types of dynamic methods either, those dont have a declaring type Queue(aMethod.DeclaringType, xMethodFullName, "Declaring Type"); } } if (aMethod is System.Reflection.MethodInfo) { Queue(((System.Reflection.MethodInfo)aMethod).ReturnType, xMethodFullName, "Return Type"); } // Scan virtuals #region Virtuals scan if (!xIsDynamicMethod && aMethod.IsVirtual) { // For virtuals we need to climb up the type tree // and find the top base method. We then add that top // node to the mVirtuals list. We don't need to add the // types becuase adding DeclaringType will already cause // all ancestor types to be added. var xVirtMethod = aMethod; var xVirtType = aMethod.DeclaringType; MethodBase xNewVirtMethod; while (true) { xVirtType = xVirtType.BaseType; if (xVirtType == null) { // We've reached object, can't go farther xNewVirtMethod = null; } else { xNewVirtMethod = xVirtType.GetMethod(aMethod.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, xParamTypes, null); if (xNewVirtMethod != null) { if (!xNewVirtMethod.IsVirtual) { // This can happen if a virtual "replaces" a non virtual // above it that is not virtual. xNewVirtMethod = null; } } } // We dont bother to add these to Queue, because we have to do a // full downlevel scan if its a new base virtual anyways. if (xNewVirtMethod == null) { // If its already in the list, we mark it null // so we dont do a full downlevel scan. if (mVirtuals.Contains(xVirtMethod)) { xVirtMethod = null; } break; } xVirtMethod = xNewVirtMethod; } // New virtual base found, we need to downscan it // If it was already in mVirtuals, then ScanType will take // care of new additions. if (xVirtMethod != null) { Queue(xVirtMethod, xMethodFullName, "Virtual Base"); mVirtuals.Add(xVirtMethod); // List changes as we go, cant be foreach for (int i = 0; i < mItemsList.Count; i++) { if (mItemsList[i] is Type) { var xType = (Type)mItemsList[i]; if (xType.IsSubclassOf(xVirtMethod.DeclaringType)) { var xNewMethod = xType.GetMethod(aMethod.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, xParamTypes, null); if (xNewMethod != null) { // We need to check IsVirtual, a non virtual could // "replace" a virtual above it? if (xNewMethod.IsVirtual) { Queue(xNewMethod, xMethodFullName, "Virtual Downscan"); } } } } } } } #endregion MethodBase xPlug = null; // Plugs may use plugs, but plugs won't be plugged over themself if (!aIsPlug && !xIsDynamicMethod) { // Check to see if method is plugged, if it is we don't scan body xPlug = mPlugManager.ResolvePlug(aMethod, xParamTypes); } if (xPlug == null) { bool xNeedsPlug = false; if ((aMethod.Attributes & MethodAttributes.PinvokeImpl) != 0) { // pinvoke methods dont have an embedded implementation xNeedsPlug = true; } else { var xImplFlags = aMethod.GetMethodImplementationFlags(); // todo: prob even more if ((xImplFlags & MethodImplAttributes.Native) != 0 || (xImplFlags & MethodImplAttributes.InternalCall) != 0) { // native implementations cannot be compiled xNeedsPlug = true; } } if (xNeedsPlug) { throw new Exception("Native code encountered, plug required. Please see http://cosmos.codeplex.com/wikipage?title=Plugs). " + LabelName.GenerateFullName(aMethod) + "." + Environment.NewLine + " Called from :" + Environment.NewLine + sourceItem); } //TODO: As we scan each method, we could update or put in a new list // that has the resolved plug so we don't have to reresolve it again // later for compilation. // Scan the method body for more type and method refs //TODO: Dont queue new items if they are plugged // or do we need to queue them with a resolved ref in a new list? InlineAttribute inl = null; foreach (InlineAttribute inli in aMethod.GetCustomAttributes(typeof(InlineAttribute), false)) { inl = inli; } if (inl != null) { return; // cancel inline } List <ILOpCode> xOpCodes; xOpCodes = mReader.ProcessMethod(aMethod); if (xOpCodes != null) { ProcessInstructions(xOpCodes); foreach (var xOpCode in xOpCodes) { if (xOpCode is ILOpCodes.OpMethod) { Queue(((ILOpCodes.OpMethod)xOpCode).Value, xMethodFullName, "Call", sourceItem); } else if (xOpCode is ILOpCodes.OpType) { Queue(((ILOpCodes.OpType)xOpCode).Value, xMethodFullName, "OpCode Value"); } else if (xOpCode is ILOpCodes.OpField) { var xOpField = (ILOpCodes.OpField)xOpCode; //TODO: Need to do this? Will we get a ILOpCodes.OpType as well? Queue(xOpField.Value.DeclaringType, xMethodFullName, "OpCode Value"); if (xOpField.Value.IsStatic) { //TODO: Why do we add static fields, but not instance? // AW: instance fields are "added" always, as part of a type, but for static fields, we need to emit a datamember Queue(xOpField.Value, xMethodFullName, "OpCode Value"); } } else if (xOpCode is ILOpCodes.OpToken) { var xTokenOp = (ILOpCodes.OpToken)xOpCode; if (xTokenOp.ValueIsType) { Queue(xTokenOp.ValueType, xMethodFullName, "OpCode Value"); } if (xTokenOp.ValueIsField) { Queue(xTokenOp.ValueField.DeclaringType, xMethodFullName, "OpCode Value"); if (xTokenOp.ValueField.IsStatic) { //TODO: Why do we add static fields, but not instance? // AW: instance fields are "added" always, as part of a type, but for static fields, we need to emit a datamember Queue(xTokenOp.ValueField, xMethodFullName, "OpCode Value"); } } } } } } }
protected void Assemble() { foreach (var xItem in mItems) { if (xItem is MethodBase) { var xMethod = (MethodBase)xItem; var xParams = xMethod.GetParameters(); var xParamTypes = xParams.Select(q => q.ParameterType).ToArray(); var xPlug = mPlugManager.ResolvePlug(xMethod, xParamTypes); var xMethodType = MethodInfo.TypeEnum.Normal; Type xPlugAssembler = null; MethodInfo xPlugInfo = null; if (xPlug != null) { xMethodType = MethodInfo.TypeEnum.NeedsPlug; PlugMethodAttribute xAttrib = null; foreach (PlugMethodAttribute attrib in xPlug.GetCustomAttributes(typeof(PlugMethodAttribute), true)) { xAttrib = attrib; } if (xAttrib != null) { xPlugAssembler = xAttrib.Assembler; xPlugInfo = new MethodInfo(xPlug, (uint)mItemsList.IndexOf(xPlug), MethodInfo.TypeEnum.Plug, null, xPlugAssembler); var xMethodInfo = new MethodInfo(xMethod, (uint)mItemsList.IndexOf(xMethod), xMethodType, xPlugInfo /*, xPlugAssembler*/); if (xAttrib != null && xAttrib.IsWildcard) { xPlugInfo.PluggedMethod = xMethodInfo; var xInstructions = mReader.ProcessMethod(xPlug); if (xInstructions != null) { ProcessInstructions(xInstructions); mAsmblr.ProcessMethod(xPlugInfo, xInstructions); } } mAsmblr.GenerateMethodForward(xMethodInfo, xPlugInfo); } else { InlineAttribute inl = null; foreach (InlineAttribute inli in xPlug.GetCustomAttributes(typeof(InlineAttribute), false)) { inl = inli; } if (inl != null) { xPlugInfo = new MethodInfo(xPlug, (uint)mItemsList.IndexOf(xItem), MethodInfo.TypeEnum.Plug, null, true); var xMethodInfo = new MethodInfo(xMethod, (uint)mItemsList.IndexOf(xMethod), xMethodType, xPlugInfo /*, xPlugAssembler*/); xPlugInfo.PluggedMethod = xMethodInfo; var xInstructions = mReader.ProcessMethod(xPlug); if (xInstructions != null) { ProcessInstructions(xInstructions); mAsmblr.ProcessMethod(xPlugInfo, xInstructions); } mAsmblr.GenerateMethodForward(xMethodInfo, xPlugInfo); } else { xPlugInfo = new MethodInfo(xPlug, (uint)mItemsList.IndexOf(xPlug), MethodInfo.TypeEnum.Plug, null, xPlugAssembler); var xMethodInfo = new MethodInfo(xMethod, (uint)mItemsList.IndexOf(xMethod), xMethodType, xPlugInfo /*, xPlugAssembler*/); if (xAttrib != null && xAttrib.IsWildcard) { xPlugInfo.PluggedMethod = xMethodInfo; var xInstructions = mReader.ProcessMethod(xPlug); if (xInstructions != null) { ProcessInstructions(xInstructions); mAsmblr.ProcessMethod(xPlugInfo, xInstructions); } } mAsmblr.GenerateMethodForward(xMethodInfo, xPlugInfo); } } } else { PlugMethodAttribute xAttrib = null; foreach (PlugMethodAttribute attrib in xMethod.GetCustomAttributes(typeof(PlugMethodAttribute), true)) { xAttrib = attrib; } if (xAttrib != null) { if (xAttrib.IsWildcard) { continue; } xPlugAssembler = xAttrib.Assembler; } var xMethodInfo = new MethodInfo(xMethod, (uint)mItemsList.IndexOf(xMethod), xMethodType, xPlugInfo, xPlugAssembler); var xInstructions = mReader.ProcessMethod(xMethod); if (xInstructions != null) { ProcessInstructions(xInstructions); mAsmblr.ProcessMethod(xMethodInfo, xInstructions); } } } else if (xItem is FieldInfo) { mAsmblr.ProcessField((FieldInfo)xItem); } } var xTypes = new HashSet <Type>(); var xMethods = new HashSet <MethodBase>(); foreach (var xItem in mItems) { if (xItem is MethodBase) { xMethods.Add((MethodBase)xItem); } else if (xItem is Type) { xTypes.Add((Type)xItem); } } mAsmblr.GenerateVMTCode(xTypes, xMethods, GetTypeUID, x => GetMethodUID(x, false)); }
public MethodBase ResolvePlug(Type aTargetType, List <Type> aImpls, MethodBase aMethod, Type[] aParamTypes) { //TODO: This method is "reversed" from old - remember that when porting MethodBase xResult = null; // Setup param types for search Type[] xParamTypes; if (aMethod.IsStatic) { xParamTypes = aParamTypes; } else { // If its an instance method, we have to add this to the ParamTypes to search xParamTypes = new Type[aParamTypes.Length + 1]; if (aParamTypes.Length > 0) { aParamTypes.CopyTo(xParamTypes, 1); } xParamTypes[0] = aTargetType; } PlugMethodAttribute xAttrib = null; foreach (var xImpl in aImpls) { // TODO: cleanup this loop, next statement shouldnt be neccessary if (xResult != null) { break; } // Plugs methods must be static, and public // Search for non signature matches first since signature searches are slower xResult = xImpl.GetMethod(aMethod.Name, BindingFlags.Static | BindingFlags.Public , null, xParamTypes, null); if (xResult == null && aMethod.Name == ".ctor") { xResult = xImpl.GetMethod("Ctor", BindingFlags.Static | BindingFlags.Public , null, xParamTypes, null); } if (xResult == null && aMethod.Name == ".cctor") { xResult = xImpl.GetMethod("CCtor", BindingFlags.Static | BindingFlags.Public , null, xParamTypes, null); } if (xResult == null) { // Search by signature foreach (var xSigMethod in xImpl.GetMethods(BindingFlags.Static | BindingFlags.Public)) { // TODO: Only allow one, but this code for now takes the last one // if there is more than one xAttrib = null; foreach (PlugMethodAttribute x in xSigMethod.GetCustomAttributes(typeof(PlugMethodAttribute), false)) { xAttrib = x; } if (xAttrib != null && (xAttrib.IsWildcard && !xAttrib.WildcardMatchParameters)) { MethodBase xTargetMethod = null; if (String.Compare(xSigMethod.Name, "Ctor", true) == 0 || String.Compare(xSigMethod.Name, "Cctor", true) == 0) { xTargetMethod = aTargetType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).SingleOrDefault(); } else { xTargetMethod = (from item in aTargetType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance) where item.Name == xSigMethod.Name select item).SingleOrDefault(); } if (xTargetMethod == aMethod) { xResult = xSigMethod; } } else { var xParams = xSigMethod.GetParameters(); //TODO: Static method plugs dont seem to be separated // from instance ones, so the only way seems to be to try // to match instance first, and if no match try static. // I really don't like this and feel we need to find // an explicit way to determine or mark the method // implementations. // // Plug implementations take "this" as first argument // so when matching we don't include it in the search Type[] xTypesInst = null; var xActualParamCount = xParams.Length; foreach (var xParam in xParams) { if (xParam.GetCustomAttributes(typeof(FieldAccessAttribute), false).Length > 0) { xActualParamCount--; } } Type[] xTypesStatic = new Type[xActualParamCount]; // If 0 params, has to be a static plug so we skip // any copying and leave xTypesInst = null // If 1 params, xTypesInst must be converted to Type[0] if (xActualParamCount == 1) { xTypesInst = new Type[0]; var xReplaceType = xParams[0].GetCustomAttributes(typeof(FieldTypeAttribute), false); if (xReplaceType.Length == 1) { xTypesStatic[0] = Type.GetType(((FieldTypeAttribute)xReplaceType[0]).Name, true); } else { xTypesStatic[0] = xParams[0].ParameterType; } } else if (xActualParamCount > 1) { xTypesInst = new Type[xActualParamCount - 1]; var xCurIdx = 0; foreach (var xParam in xParams.Skip(1)) { if (xParam.GetCustomAttributes(typeof(FieldAccessAttribute), false).Length > 0) { continue; } var xReplaceType = xParam.GetCustomAttributes(typeof(FieldTypeAttribute), false); if (xReplaceType.Length == 1) { xTypesInst[xCurIdx] = Type.GetType(((FieldTypeAttribute)xReplaceType[0]).Name, true); } else { xTypesInst[xCurIdx] = xParam.ParameterType; } xCurIdx++; } xCurIdx = 0; foreach (var xParam in xParams) { if (xParam.GetCustomAttributes(typeof(FieldAccessAttribute), false).Length > 0) { xCurIdx++; continue; } if (xCurIdx >= xTypesStatic.Length) { break; } xTypesStatic[xCurIdx] = xParam.ParameterType; xCurIdx++; } } SysReflection.MethodBase xTargetMethod = null; // TODO: In future make rule that all ctor plugs are called // ctor by name, or use a new attrib //TODO: Document all the plug stuff in a document on website //TODO: To make inclusion of plugs easy, we can make a plugs master // that references the other default plugs so user exes only // need to reference that one. // TODO: Skip FieldAccessAttribute if in impl if (xTypesInst != null) { if (string.Compare(xSigMethod.Name, "ctor", true) == 0) { xTargetMethod = aTargetType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, xTypesInst, null); } else { xTargetMethod = aTargetType.GetMethod(xSigMethod.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, xTypesInst, null); } } // Not an instance method, try static if (xTargetMethod == null) { if (string.Compare(xSigMethod.Name, "cctor", true) == 0 || string.Compare(xSigMethod.Name, "ctor", true) == 0) { xTargetMethod = aTargetType.GetConstructor(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, xTypesStatic, null); } else { xTargetMethod = aTargetType.GetMethod(xSigMethod.Name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, xTypesStatic, null); } } if (xTargetMethod == aMethod) { xResult = xSigMethod; break; } if (xAttrib != null && xAttrib.Signature != null) { var xName = DataMember.FilterStringForIncorrectChars(LabelName.GenerateFullName(aMethod)); if (string.Compare(xName, xAttrib.Signature, true) == 0) { xResult = xSigMethod; break; } } xAttrib = null; } } } else { // check if signatur is equal var xResPara = xResult.GetParameters(); var xAMethodPara = aMethod.GetParameters(); if (aMethod.IsStatic) { if (xResPara.Length != xAMethodPara.Length) { return(null); } } else { if (xResPara.Length - 1 != xAMethodPara.Length) { return(null); } } for (int i = 0; i < xAMethodPara.Length; i++) { int correctIndex = aMethod.IsStatic ? i : i + 1; if (xResPara[correctIndex].ParameterType != xAMethodPara[i].ParameterType) { return(null); } } if (xResult.Name == "Ctor" && aMethod.Name == ".ctor") { } else if (xResult.Name == "CCtor" && aMethod.Name == ".cctor") { } else if (xResult.Name != aMethod.Name) { return(null); } } } if (xResult == null) { return(null); } // If we found a matching method, check for attributes // that might disable it. //TODO: For signature ones, we could cache the attrib. Thats // why we check for null here if (xAttrib == null) { // TODO: Only allow one, but this code for now takes the last one // if there is more than one foreach (PlugMethodAttribute x in xResult.GetCustomAttributes(typeof(PlugMethodAttribute), false)) { xAttrib = x; } } // See if we need to disable this plug if (xAttrib != null) { if (!xAttrib.Enabled) { //xResult = null; return(null); } else if (xAttrib.IsMonoOnly) { //TODO: Check this against build options //TODO: Two exclusive IsOnly's dont make sense // refactor these as a positive rather than negative // Same thing at type plug level //xResult = null; return(null); } //else if (xAttrib.Signature != null) { // var xName = DataMember.FilterStringForIncorrectChars(MethodInfoLabelGenerator.GenerateFullName(xResult)); // if (string.Compare(xName, xAttrib.Signature, true) != 0) { // xResult = null; // } //} } InlineAttribute xInlineAttrib = null; foreach (InlineAttribute inli in xResult.GetCustomAttributes(typeof(InlineAttribute), false)) { xInlineAttrib = inli; } if (xInlineAttrib == null) { if (Queue != null) { CompilerHelpers.Debug("Queueing Plug:", xResult.DeclaringType, "::", xResult.Name); Queue(xResult, null, "Plug Method"); } } //if (xAttrib != null && xAttrib.Signature != null) //{ // var xTargetMethods = aTargetType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); // //System_Void__Indy_IL2CPU_Assembler_Assembler__cctor__ // //If signature exists, the search is slow. Signatures // //are infrequent though, so for now we just go slow method // //and have not optimized or cached this info. When we // //redo the plugs, we can fix this. // bool xEnabled=true; // foreach (var xTargetMethod in xTargetMethods) // { // string sName = DataMember.FilterStringForIncorrectChars(MethodInfoLabelGenerator.GenerateFullName(xTargetMethod)); // if (string.Compare(sName, xAttrib.Signature, true) == 0) // { // //uint xUID = QueueMethod(xPlugImpl.Plug, "Plug", xMethod, true); // //mMethodPlugs.Add(xTargetMethod, new PlugInfo(xUID, xAttrib.Assembler)); // // Mark as disabled, because we already handled it // xEnabled = false; // break; // } // } // // if still enabled, we didn't find our method // if (xEnabled) // { // // todo: more precise error: imagine having a 100K line project, and this error happens... // throw new Exception("Plug target method not found."); // } //} return(xResult); }