protected void ScanQueue() { while (mQueue.Count > 0) { var xItem = mQueue.Dequeue(); CompilerHelpers.Debug($"ILScanner: ScanQueue - '{xItem}'"); // Check for MethodBase first, they are more numerous // and will reduce compares if (xItem.Item is MethodBase xMethod) { ScanMethod(xMethod, false, xItem.SourceItem); } else if (xItem.Item is Type xType) { ScanType(xType); // Methods and fields cant exist without types, so we only update // mUsedAssemblies in type branch. if (!mUsedAssemblies.Contains(xType.Assembly)) { mUsedAssemblies.Add(xType.Assembly); } } else if (xItem.Item is FieldInfo) { // todo: static fields need more processing? } else { throw new Exception("Unknown item found in queue."); } } }
public void Execute(MethodBase[] aBootEntries, List <MemberInfo> aForceIncludes, IEnumerable <Assembly> plugsAssemblies) { foreach (var xBootEntry in aBootEntries) { Queue(xBootEntry.DeclaringType, null, "Boot Entry Declaring Type"); Queue(xBootEntry, null, "Boot Entry"); } foreach (var xForceInclude in aForceIncludes) { Queue(xForceInclude, null, "Force Include"); } mPlugManager.FindPlugImpls(plugsAssemblies); // Now that we found all plugs, scan them. // We have to scan them after we find all plugs, because // plugs can use other plugs mPlugManager.ScanFoundPlugs(); foreach (var xPlug in mPlugManager.PlugImpls) { CompilerHelpers.Debug($"Plug found: '{xPlug.Key.FullName}' in '{xPlug.Key.Assembly.FullName}'"); } ILOp.PlugManager = mPlugManager; // Pull in extra implementations, GC etc. Queue(RuntimeEngineRefs.InitializeApplicationRef, null, "Explicit Entry"); Queue(RuntimeEngineRefs.FinalizeApplicationRef, null, "Explicit Entry"); Queue(VTablesImplRefs.SetMethodInfoRef, null, "Explicit Entry"); Queue(VTablesImplRefs.IsInstanceRef, null, "Explicit Entry"); Queue(VTablesImplRefs.SetTypeInfoRef, null, "Explicit Entry"); Queue(VTablesImplRefs.SetInterfaceInfoRef, null, "Explicit Entry"); Queue(VTablesImplRefs.SetInterfaceMethodInfoRef, null, "Explicit Entry"); Queue(VTablesImplRefs.GetMethodAddressForTypeRef, null, "Explicit Entry"); Queue(VTablesImplRefs.GetMethodAddressForInterfaceTypeRef, null, "Explicit Entry"); Queue(GCImplementationRefs.IncRefCountRef, null, "Explicit Entry"); Queue(GCImplementationRefs.DecRefCountRef, null, "Explicit Entry"); Queue(GCImplementationRefs.AllocNewObjectRef, null, "Explicit Entry"); // Pull in Array constructor Queue(typeof(Array).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).First(), null, "Explicit Entry"); // Pull in MulticastDelegate.GetInvocationList, needed by the Invoke plug Queue(typeof(MulticastDelegate).GetMethod("GetInvocationList"), null, "Explicit Entry"); // Exception support Queue(ExceptionHelperRefs.CurrentExceptionRef, null, "Explicit Entry"); Queue(ExceptionHelperRefs.ThrowNotFiniteNumberExceptionRef, null, "Explicit Entry"); mAsmblr.ProcessField(typeof(string).GetField("Empty", BindingFlags.Static | BindingFlags.Public)); ScanQueue(); UpdateAssemblies(); Assemble(); mAsmblr.EmitEntrypoint(null, aBootEntries); }
protected void Queue(MemberInfo aItem, object aSrc, string aSrcType, string sourceItem = null) { CompilerHelpers.Debug($"Enqueing: {aItem.DeclaringType?.Name ?? ""}.{aItem.Name} from {aSrc}"); if (aItem == null) { throw new ArgumentNullException(nameof(aItem)); } //TODO: fix this, as each label/symbol should also contain an assembly specifier. //if ((xMemInfo != null) && (xMemInfo.DeclaringType != null) // && (xMemInfo.DeclaringType.FullName == "System.ThrowHelper") // && (xMemInfo.DeclaringType.Assembly.GetName().Name != "mscorlib")) //{ // System.ThrowHelper exists in MS .NET twice... // Its an internal class that exists in both mscorlib and system assemblies. // They are separate types though, so normally the scanner scans both and // then we get conflicting labels. MS included it twice to make exception // throwing code smaller. They are internal though, so we cannot // reference them directly and only via finding them as they come along. // We find it here, not via QueueType so we only check it here. Later // we might have to checkin QueueType also. // So now we accept both types, but emit code for only one. This works // with the current Nasm assembler as we resolve by name in the assembler. // However with other assemblers this approach may not work. // If AssemblerNASM adds assembly name to the label, this will allow // both to exist as they do in BCL. // So in the future we might be able to remove this hack, or change // how it works. // // Do nothing // //} /*else*/ if (!mItems.Contains(aItem)) { if (mLogEnabled) { LogMapPoint(aSrc, aSrcType, aItem); } mItems.Add(aItem); mItemsList.Add(aItem); if (aSrc is MethodBase xMethodBaseSrc) { aSrc = xMethodBaseSrc.DeclaringType + "::" + aSrc; } mQueue.Enqueue(new ScannerQueueItem(aItem, aSrcType, aSrc + Environment.NewLine + sourceItem)); } }
public void Execute(SysReflection.MethodBase aStartMethod) { if (aStartMethod == null) { throw new ArgumentNullException("aStartMethod"); } // TODO: Investigate using MS CCI // Need to check license, as well as in profiler // http://cciast.codeplex.com/ #region Description // Methodology // // Ok - we've done the scanner enough times to know it needs to be // documented super well so that future changes won't inadvertently // break undocumented and unseen requirements. // // We've tried many approaches including recursive and additive scanning. // They typically end up being inefficient, overly complex, or both. // // -We would like to scan all types/methods so we can plug them. // -But we can't scan them until we plug them, because we will scan things // that plugs would remove/change the paths of. // -Plugs may also call methods which are also plugged. // -We cannot resolve plugs ahead of time but must do on the fly during // scanning. // -TODO: Because we do on the fly resolution, we need to add explicit // checking of plug classes and err when public methods are found that // do not resolve. Maybe we can make a list and mark, or rescan. Can be done // later or as an optional auditing step. // // This why in the past we had repetitive scans. // // Now we focus on more passes, but simpler execution. In the end it should // be eaiser to optmize and yield overall better performance. Most of the // passes should be low overhead versus an integrated system which often // would need to reiterate over items multiple times. So we do more loops on // with less repetitive analysis, instead of fewer loops but more repetition. // // -Locate all plug classes // -Scan from entry point collecting all types and methods while checking // for and following plugs // -For each type // -Include all ancestors // -Include all static constructors // -For each virtual method // -Scan overloads in descendants until IsFinal, IsSealed or end // -Scan base in ancestors until top or IsAbstract // -Go to scan types again, until no new ones found. // -Because the virtual method scanning will add to the list as it goes, maintain // 2 lists. // -Known Types and Methods // -Types and Methods in Queue - to be scanned // -Finally, do compilation #endregion mPlugManager.FindPlugImpls(); // Now that we found all plugs, scan them. // We have to scan them after we find all plugs, because // plugs can use other plugs mPlugManager.ScanFoundPlugs(); foreach (var xPlug in mPlugManager.PlugImpls) { CompilerHelpers.Debug($"Plug found: '{xPlug.Key.FullName}'"); } ILOp.mPlugManager = mPlugManager; // Pull in extra implementations, GC etc. Queue(RuntimeEngineRefs.InitializeApplicationRef, null, "Explicit Entry"); Queue(RuntimeEngineRefs.FinalizeApplicationRef, null, "Explicit Entry"); //Queue(typeof(CosmosAssembler).GetMethod("PrintException"), null, "Explicit Entry"); Queue(VTablesImplRefs.SetMethodInfoRef, null, "Explicit Entry"); Queue(VTablesImplRefs.IsInstanceRef, null, "Explicit Entry"); Queue(VTablesImplRefs.SetTypeInfoRef, null, "Explicit Entry"); Queue(VTablesImplRefs.GetMethodAddressForTypeRef, null, "Explicit Entry"); Queue(GCImplementationRefs.IncRefCountRef, null, "Explicit Entry"); Queue(GCImplementationRefs.DecRefCountRef, null, "Explicit Entry"); Queue(GCImplementationRefs.AllocNewObjectRef, null, "Explicit Entry"); // for now, to ease runtime exception throwing Queue(typeof(ExceptionHelper).GetMethod("ThrowNotImplemented", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null), null, "Explicit Entry"); Queue(typeof(ExceptionHelper).GetMethod("ThrowOverflow", BindingFlags.Static | BindingFlags.Public, null, new Type[] { }, null), null, "Explicit Entry"); Queue(RuntimeEngineRefs.InitializeApplicationRef, null, "Explicit Entry"); Queue(RuntimeEngineRefs.FinalizeApplicationRef, null, "Explicit Entry"); // register system types: Queue(typeof(Array), null, "Explicit Entry"); Queue(typeof(Array).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null), null, "Explicit Entry"); var xThrowHelper = Type.GetType("System.ThrowHelper", true); Queue(xThrowHelper.GetMethod("ThrowInvalidOperationException", BindingFlags.NonPublic | BindingFlags.Static), null, "Explicit Entry"); Queue(typeof(MulticastDelegate).GetMethod("GetInvocationList"), null, "Explicit Entry"); Queue(ExceptionHelperRefs.CurrentExceptionRef, null, "Explicit Entry"); //System_Delegate____System_MulticastDelegate_GetInvocationList__ // Start from entry point of this program Queue(aStartMethod, null, "Entry Point"); ScanQueue(); UpdateAssemblies(); Assemble(); mAsmblr.EmitEntrypoint(aStartMethod); }
/// <summary>Load every refernced assemblies that have an associated FullPath property and seek for /// the kernel default constructor.</summary> /// <returns>The kernel default constructor or a null reference if either none or several such /// constructor could be found.</returns> private MethodBase LoadAssemblies() { // Try to load explicit path references. // These are the references of our boot project. We dont actually ever load the boot // project asm. Instead the references will contain plugs, and the kernel. We load // them then find the entry point in the kernel. // // Plugs and refs in this list will be loaded absolute (or as proj refs) only. Asm resolution // will not be tried on them, but will on ASMs they reference. string xKernelBaseName = "Cosmos.System.Kernel"; LogMessage("Kernel Base: " + xKernelBaseName); Type xKernelType = null; foreach (string xRef in References) { LogMessage("Checking Reference: " + xRef); if (File.Exists(xRef)) { LogMessage(" Exists"); var xAssembly = AssemblyLoadContext.Default.LoadFromAssemblyCacheOrPath(xRef); CompilerHelpers.Debug($"Looking for kernel in {xAssembly}"); foreach (var xType in xAssembly.ExportedTypes) { if (!xType.IsGenericTypeDefinition && !xType.IsAbstract) { CompilerHelpers.Debug($"Checking type {xType.FullName}"); // We used to resolve with this: // if (xType.IsSubclassOf(typeof(Cosmos.System.Kernel))) { // But this caused a single dependency on Cosmos.System which is bad. // We could use an attribute, or maybe an interface would be better in this limited case. Interface // will force user to implement what is needed if replacing our core. But in the end this is a "not needed" feature // and would only complicate things. // So for now at least, we look by name so we dont have a dependency since the method returns a MethodBase and not a Kernel instance anyway. if (xType.BaseType.FullName == xKernelBaseName) { if (xKernelType != null) { LogError($"Two kernels found: {xType.FullName} and {xKernelType.FullName}"); return(null); } xKernelType = xType; } } } } } if (xKernelType == null) { LogError("No kernel found."); return(null); } var xCtor = xKernelType.GetConstructor(Type.EmptyTypes); if (xCtor == null) { LogError("Kernel has no public parameterless constructor."); return(null); } return(xCtor); }
protected void ScanType(Type aType) { CompilerHelpers.Debug($"ILScanner: ScanType"); CompilerHelpers.Debug($"Type = '{aType}'"); // Add immediate ancestor type // We dont need to crawl up farther, when the BaseType is scanned // it will add its BaseType, and so on. if (aType.BaseType != null) { Queue(aType.BaseType, aType, "Base Type"); } // Queue static ctors // We always need static ctors, else the type cannot // be created. foreach (var xCctor in aType.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) { if (xCctor.DeclaringType == aType) { Queue(xCctor, aType, "Static Constructor"); } } // For each new type, we need to scan for possible new virtuals // in our new type if its a descendant of something in // mVirtuals. foreach (var xVirt in mVirtuals) { // See if our new type is a subclass of any virt's DeclaringTypes // If so our new type might have some virtuals if (aType.IsSubclassOf(xVirt.DeclaringType)) { var xParams = xVirt.GetParameters(); var xParamTypes = new Type[xParams.Length]; // Dont use foreach, enum generaly keeps order but // isn't guaranteed. for (int i = 0; i < xParams.Length; i++) { xParamTypes[i] = xParams[i].ParameterType; } var xMethod = aType.GetMethod(xVirt.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, xParamTypes, null); if (xMethod != null) { // We need to check IsVirtual, a non virtual could // "replace" a virtual above it? if (xMethod.IsVirtual) { Queue(xMethod, aType, "Virtual"); } } } if (!aType.IsGenericParameter && xVirt.DeclaringType.IsInterface) { if (!aType.IsInterface && aType.GetInterfaces().Contains(xVirt.DeclaringType)) { var xIntfMapping = aType.GetInterfaceMap(xVirt.DeclaringType); if ((xIntfMapping.InterfaceMethods != null) && (xIntfMapping.TargetMethods != null)) { var xIdx = Array.IndexOf(xIntfMapping.InterfaceMethods, xVirt); if (xIdx != -1) { Queue(xIntfMapping.TargetMethods[xIdx], aType, "Virtual"); } } } } } foreach (var xInterface in aType.GetInterfaces()) { Queue(xInterface, aType, "Implemented Interface"); } }
protected void ScanMethod(MethodBase aMethod, bool aIsPlug, string sourceItem) { CompilerHelpers.Debug($"ILScanner: ScanMethod"); CompilerHelpers.Debug($"Method = '{aMethod}'"); CompilerHelpers.Debug($"IsPlug = '{aIsPlug}'"); CompilerHelpers.Debug($"Source = '{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.GetFullName(aMethod); for (int i = 0; i < xParams.Length; i++) { xParamTypes[i] = xParams[i].ParameterType; Queue(xParamTypes[i], aMethod, "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, aMethod, "Declaring Type"); } } if (aMethod is MethodInfo) { Queue(((MethodInfo)aMethod).ReturnType, aMethod, "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.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(method => method.Name == aMethod.Name && method.GetParameters().Select(param => param.ParameterType) .SequenceEqual(xParamTypes)) .SingleOrDefault(); 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, aMethod, "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 xType && xType != xVirtMethod.DeclaringType && !xType.IsInterface) { if (xType.IsSubclassOf(xVirtMethod.DeclaringType)) { var xNewMethod = xType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(method => method.Name == aMethod.Name && method.GetParameters().Select(param => param.ParameterType).SequenceEqual(xParamTypes)) .SingleOrDefault(); if (xNewMethod != null) { // We need to check IsVirtual, a non virtual could // "replace" a virtual above it? if (xNewMethod.IsVirtual) { Queue(xNewMethod, aMethod, "Virtual Downscan"); } } } else if (xVirtMethod.DeclaringType.IsInterface && xType.GetInterfaces().Contains(xVirtMethod.DeclaringType)) { var xInterfaceMap = xType.GetInterfaceMap(xVirtMethod.DeclaringType); var xMethodIndex = Array.IndexOf(xInterfaceMap.InterfaceMethods, xVirtMethod); if (xMethodIndex != -1) { var xMethod = xInterfaceMap.TargetMethods[xMethodIndex]; if (xMethod.DeclaringType == xType) { Queue(xInterfaceMap.TargetMethods[xMethodIndex], aMethod, "Virtual Downscan"); } } } } } } } #endregion Virtuals scan MethodBase xPlug = null; // Plugs may use plugs, but plugs won't be plugged over themself var inl = aMethod.GetCustomAttribute <InlineAttribute>(); 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) { //ScanMethod(xPlug, true, "Plug method"); if (inl == null) { Queue(xPlug, aMethod, "Plug method"); } } } 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.HasFlag(MethodImplAttributes.Native) || xImplFlags.HasFlag(MethodImplAttributes.InternalCall)) { // native implementations cannot be compiled xNeedsPlug = true; } } if (xNeedsPlug) { throw new Exception(Environment.NewLine + "Native code encountered, plug required." + Environment.NewLine + " DO NOT REPORT THIS AS A BUG." + Environment.NewLine + " Please see http://www.gocosmos.org/docs/plugs/missing/" + Environment.NewLine + " Need plug for: " + LabelName.GetFullName(aMethod) + "." + Environment.NewLine + " Called from :" + Environment.NewLine + sourceItem + Environment.NewLine); } //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? 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, aMethod, "Call", sourceItem); } else if (xOpCode is ILOpCodes.OpType) { Queue(((ILOpCodes.OpType)xOpCode).Value, aMethod, "OpCode Value"); } else if (xOpCode is ILOpCodes.OpField xOpField) { //TODO: Need to do this? Will we get a ILOpCodes.OpType as well? Queue(xOpField.Value.DeclaringType, aMethod, "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, aMethod, "OpCode Value"); } } else if (xOpCode is ILOpCodes.OpToken xOpToken) { if (xOpToken.ValueIsType) { Queue(xOpToken.ValueType, aMethod, "OpCode Value"); } if (xOpToken.ValueIsField) { Queue(xOpToken.ValueField.DeclaringType, aMethod, "OpCode Value"); if (xOpToken.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(xOpToken.ValueField, aMethod, "OpCode Value"); } } } } } } }
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); }
/// <summary>Load every refernced assemblies that have an associated FullPath property and seek for /// the kernel default constructor.</summary> /// <returns>The kernel default constructor or a null reference if either none or several such /// constructor could be found.</returns> private MethodBase LoadAssemblies() { // Try to load explicit path references. // These are the references of our boot project. We dont actually ever load the boot // project asm. Instead the references will contain plugs, and the kernel. We load // them then find the entry point in the kernel. // // Plugs and refs in this list will be loaded absolute (or as proj refs) only. Asm resolution // will not be tried on them, but will on ASMs they reference. // AssemblyLoadContext.Default.Resolving += Default_Resolving; mLoadedExtensions = new List <CompilerExtensionBase>(); Type xKernelType = null; foreach (string xReference in References) { if (File.Exists(xReference)) { var xAssembly = AssemblyLoadContext.Default.LoadFromAssemblyCacheOrPath(xReference); CompilerHelpers.Debug($"Looking for kernel in '{xAssembly}'"); foreach (var xType in xAssembly.ExportedTypes) { if (!xType.GetTypeInfo().IsGenericTypeDefinition&& !xType.GetTypeInfo().IsAbstract) { CompilerHelpers.Debug($"Checking type '{xType.FullName}'"); if (xType.GetTypeInfo().IsSubclassOf(typeof(Cosmos.System.Kernel))) { // found kernel? if (xKernelType != null) { // already a kernel found, which is not supported. LogError($"Two kernels found! '{xType.AssemblyQualifiedName}' and '{xKernelType.AssemblyQualifiedName}'"); return(null); } xKernelType = xType; } } } var xCompilerExtensionsMetas = xAssembly.GetCustomAttributes <CompilerExtensionAttribute>(); foreach (var xMeta in xCompilerExtensionsMetas) { mLoadedExtensions.Add((CompilerExtensionBase)Activator.CreateInstance(xMeta.Type)); } } } if (xKernelType == null) { LogError("No Kernel found!"); return(null); } var xCtor = xKernelType.GetTypeInfo().GetConstructor(Type.EmptyTypes); if (xCtor == null) { LogError("Kernel has no public default constructor"); return(null); } return(xCtor); }