Beispiel #1
0
        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], 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 SysReflection.MethodInfo)
            {
                Queue(((SysReflection.MethodInfo)aMethod).ReturnType, aMethod, "Return Type");
            }
            if (aMethod.GetFullName().IndexOf("CreateComparer", StringComparison.OrdinalIgnoreCase) != -1)
            {
                ;
            }
            // 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, 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)
                        {
                            var xType = (Type)mItemsList[i];
                            if ((xType.IsSubclassOf(xVirtMethod.DeclaringType)) || ((xVirtMethod.DeclaringType.IsInterface) && (xVirtMethod.DeclaringType.IsAssignableFrom(xType))))
                            {
                                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, aMethod, "Virtual Downscan");
                                    }
                                }
                            }
                        }
                    }
                }
            }
            #endregion

            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 & 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 https://github.com/CosmosOS/Cosmos/wiki/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?

                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)
                        {
                            var xOpField = (ILOpCodes.OpField)xOpCode;
                            //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)
                        {
                            var xTokenOp = (ILOpCodes.OpToken)xOpCode;
                            if (xTokenOp.ValueIsType)
                            {
                                Queue(xTokenOp.ValueType, aMethod, "OpCode Value");
                            }
                            if (xTokenOp.ValueIsField)
                            {
                                Queue(xTokenOp.ValueField.DeclaringType, aMethod, "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, aMethod, "OpCode Value");
                                }
                            }
                        }
                    }
                }
            }
        }
Beispiel #2
0
        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.GetMethod(aMethod.Name, xParamTypes);
                        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 enumerable = xType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                                 .Where(method => method.Name == aMethod.Name &&
                                                        method.GetParameters().Select(param => param.ParameterType).SequenceEqual(xParamTypes));
                                // We need to check IsVirtual, a non virtual could
                                // "replace" a virtual above it?
                                var xNewMethod = enumerable.FirstOrDefault(m => m.IsVirtual);
                                while (xNewMethod != null && (xNewMethod.Attributes & MethodAttributes.NewSlot) != 0)
                                {
                                    xType      = xType.BaseType;
                                    xNewMethod = enumerable.Where(m => m.DeclaringType == xType).SingleOrDefault();
                                }
                                if (xNewMethod != null)
                                {
                                    Queue(xNewMethod, aMethod, "Virtual Downscan");
                                }
                            }
                            else if (xVirtMethod.DeclaringType.IsInterface &&
                                     xType.GetInterfaces().Contains(xVirtMethod.DeclaringType) &&
                                     (xType.BaseType != typeof(Array) || !xVirtMethod.DeclaringType.IsGenericType))
                            {
                                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) + "(Plug Signature: " + DataMember.FilterStringForIncorrectChars(LabelName.GetFullName(aMethod)) + " ). " + Environment.NewLine
                                        + "  Static: " + aMethod.IsStatic + Environment.NewLine
                                        + "  Assembly: " + aMethod.DeclaringType.Assembly.FullName + 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");
                                }
                            }
                        }
                    }
                }
            }
        }