Esempio n. 1
0
        private void CheckMethodBody(MethodDefinition method)
        {
            var synchronizedEvents = new Dictionary <MethodReference, List <MethodReference> > ();
            var thisSynchronized   = new List <TypeReference> ();

            foreach (Instruction ins in method.Body.Instructions)
            {
                MethodReference candidate = null;

                switch (ins.OpCode.Code)
                {
                case Code.Newobj:
                    if (ins.Previous != null && ins.Previous.OpCode.Code == Code.Ldftn)
                    {
                        MethodReference ctor = (MethodReference)ins.Operand;
                        TypeReference   type = ctor.DeclaringType;
                        if (type.IsDelegate())
                        {
                            string nspace = type.Namespace;
                            // ldftn entry-point
                            // newobj System.Void System.Threading.XXX::.ctor (System.Object,System.IntPtr)
                            // i.e. creation of a System.Threading delegate
                            if (nspace == "System.Threading")
                            {
                                string name = type.Name;
                                if (name == "ThreadStart" ||
                                    name == "ParameterizedThreadStart" ||
                                    name == "WaitCallback" ||
                                    name == "WaitOrTimerCallback" ||
                                    name == "TimerCallback")
                                {
                                    candidate = (MethodReference)ins.Previous.Operand;
                                }

                                // ldftn entry-point
                                // newobj System.Void System.AsyncCallback::.ctor (System.Object,System.IntPtr)
                                // i.e. creation of a async delegate
                            }
                            else if (nspace == "System")
                            {
                                if (type.Name == "AsyncCallback")
                                {
                                    candidate = (MethodReference)ins.Previous.Operand;
                                }

                                // ldftn entry-point
                                // newobj System.Void ThreadedDelegate::.ctor (System.Object,System.IntPtr)
                                // i.e. creation of a delegate which is decorated with a threading attribute
                            }
                            else if (!ThreadRocks.ThreadedNamespace(nspace))
                            {
                                // Delegates must be able to call the methods they are bound to.
                                MethodDefinition target = ((MethodReference)ins.Previous.Operand).Resolve();
                                if (target != null)
                                {
                                    ThreadModel callerModel = type.ThreadingModel();
                                    if (!target.IsGeneratedCode() || target.IsProperty())
                                    {
                                        ThreadModel targetModel = target.ThreadingModel();
                                        if (!IsValidCall(callerModel, targetModel))
                                        {
                                            string mesg = String.Format(CultureInfo.InvariantCulture,
                                                                        "{0} delegate cannot be bound to {1} {2} method.",
                                                                        callerModel, targetModel, target.Name);
                                            ++DefectCount;
                                            Log.WriteLine(this, "Defect: {0}", mesg);
                                            Defect defect = new Defect(this, method, method, ins, Severity.High, Confidence.High, mesg);
                                            Runner.Report(defect);
                                        }
                                    }
                                    else if (!callerModel.Is(ThreadModel.MainThread))
                                    {
                                        anonymous_entry_points.Add(target);
                                    }
                                }
                            }
                        }
                    }
                    break;

                case Code.Call:
                case Code.Callvirt:
                    if (!method.IsGeneratedCode() || method.IsProperty())
                    {
                        CheckForLegalCall(method, ins);
                    }

                    // ldftn entry-point
                    // newobj XXX
                    // callvirt System.Void SynchronizedType::add_Name (XXX)
                    // i.e. adding a delegate to an event in a type which uses SynchronizingObject
                    MethodReference call      = (MethodReference)ins.Operand;
                    TypeReference   call_type = call.DeclaringType;
                    if (ins.Previous.Is(Code.Newobj) && ins.Previous.Previous.Is(Code.Ldftn))
                    {
                        // A few events are blacklisted because they do not use SynchronizingObject and
                        // are therefore always threaded.
                        if (IsNonSynchronizedSetter(call))
                        {
                            candidate = (MethodReference)ins.Previous.Previous.Operand;

                            // But most events do use SynchronizingObject and therefore their threading
                            // depends on whether and how SynchronizingObject is initialized.
                        }
                        else if (HasSynchronizingObject(call_type))
                        {
                            List <MethodReference> methods;
                            if (!synchronizedEvents.TryGetValue(call, out methods))
                            {
                                methods = new List <MethodReference> ();
                                synchronizedEvents.Add(call, methods);
                            }

                            methods.AddIfNew((MethodReference)ins.Previous.Previous.Operand);

                            // Misc threaded events.
                        }
                        else if (call_type.IsNamed("System.ComponentModel", "BackgroundWorker"))
                        {
                            if (call.Name == "add_DoWork")
                            {
                                candidate = (MethodReference)ins.Previous.Previous.Operand;
                            }
                        }

                        // callvirt System.Void System.Diagnostics.Process::set_SynchronizingObject (System.ComponentModel.ISynchronizeInvoke)
                    }
                    else if (SetSynchronizingObject.Matches(call))
                    {
                        if (ins.Previous.OpCode.Code == Code.Ldarg_0)
                        {
                            thisSynchronized.Add(call_type);
                        }
                    }
                    break;
                }

                if (candidate != null)
                {
                    Log.WriteLine(this, "{0} is a thread entry point", candidate);
                    CheckEntryPoint(candidate);
                }
            }

            // For every method added to a threaded event,
            ThreadModel?method_model = null;

            foreach (KeyValuePair <MethodReference, List <MethodReference> > entry in synchronizedEvents)
            {
                // if the event is synchronized on this then the target must have the same thread
                // as the current method's type or better and it should not be treated as a entry point.
                if (thisSynchronized.Contains(entry.Key.DeclaringType))
                {
                    if (method_model == null)
                    {
                        method_model = method.DeclaringType.ThreadingModel();
                    }
                    foreach (MethodReference mr in entry.Value)
                    {
                        MethodDefinition target = mr.Resolve();
                        if (target != null)
                        {
                            ThreadModel targetModel = target.ThreadingModel();
                            if (!IsValidCall(method_model.Value, targetModel))
                            {
                                string mesg = String.Format(CultureInfo.InvariantCulture,
                                                            "{0} {1} cannot be bound to {2} {3} method.",
                                                            method_model, entry.Key, targetModel, target.Name);
                                ReportDefect(method, Severity.High, Confidence.High, mesg);
                            }
                        }
                    }

                    // otherwise the method has to be treated as a thread entry point.
                }
                else
                {
                    foreach (MethodReference mr in entry.Value)
                    {
                        Log.WriteLine(this, "{0} is a thread entry point", mr);
                        CheckEntryPoint(mr);
                    }
                }
            }
        }
Esempio n. 2
0
        public RuleResult CheckMethod(MethodDefinition method)
        {
            if (ThreadRocks.ThreadedNamespace(method.DeclaringType.Namespace))
            {
                return(RuleResult.DoesNotApply);
            }

            Log.WriteLine(this);
            Log.WriteLine(this, "---------------------------------------");
            Log.WriteLine(this, method);

            string name = method.Name;
            IList <ParameterDefinition> pdc = method.HasParameters ? method.Parameters : null;

            // Finalizers need to be single threaded.
            ThreadModel model = method.ThreadingModel();

            if (method.IsFinalizer())
            {
                if ((model & ~ThreadModel.AllowEveryCaller) != ThreadModel.SingleThread)
                {
                    string mesg = "Finalizers should be decorated with [ThreadModel (ThreadModel.SingleThreaded)].";
                    ReportDefect(method, Severity.High, Confidence.High, mesg);
                }
            }

            // Make sure all of the thread entry points are properly decorated and
            // that all calls are legit.
            if (method.HasBody && opcodes_mask.Intersect(OpCodeEngine.GetBitmask(method)))
            {
                CheckMethodBody(method);
            }

            // A delegate used with a threaded event must use the same threading model
            // as the event.
            if (method.IsAddOn)
            {
                ParameterDefinition p            = pdc [0];
                TypeDefinition      delegateType = p.ParameterType.Resolve();
                if (delegateType != null && !ThreadRocks.ThreadedNamespace(delegateType.Namespace))
                {
                    ThreadModel delegateModel = delegateType.ThreadingModel();
                    if (model != delegateModel && !delegateModel.AllowsEveryCaller())
                    {
                        string mesg = String.Format(CultureInfo.InvariantCulture,
                                                    "{0} event must match {1} delegate.", model, delegateModel);
                        ReportDefect(method, Severity.High, Confidence.High, mesg);
                    }
                }
            }

            // An override of a base method or an implementation of an interface method
            // must use the same threading model as the original method.
            if (method.IsVirtual)
            {
                IEnumerable <TypeDefinition> superTypes = method.DeclaringType.AllSuperTypes();
                bool new_slot = method.IsNewSlot;
                superTypes = from s in superTypes where (s.IsInterface == new_slot) select s;
                string [] parameters = pdc != null
                                   ? (from p in pdc.Cast <ParameterDefinition> () select p.ParameterType.GetFullName()).ToArray()
                                   : null;

                string return_type_name = method.ReturnType.GetFullName();
                foreach (TypeDefinition type in superTypes)
                {
                    MethodDefinition superMethod = type.GetMethod(name, return_type_name, parameters);
                    if (superMethod != null && !ThreadRocks.ThreadedNamespace(superMethod.DeclaringType.Namespace))
                    {
                        ThreadModel superModel = superMethod.ThreadingModel();
                        if (model != superModel)
                        {
                            string mesg = String.Format(CultureInfo.InvariantCulture,
                                                        "{0} {1} must match {2} {3} method.", model, name, superModel,
                                                        new_slot ? "interface" : "base");
                            ReportDefect(method, Severity.High, Confidence.High, mesg);
                        }
                    }
                }
            }

            // Serializable cannot be applied to static methods, but can be applied to
            // operators because they're just sugar for normal calls.
            if (method.IsStatic && model.Is(ThreadModel.Serializable) && !name.StartsWith("op_", StringComparison.Ordinal))
            {
                string mesg = "Static members cannot be decorated with Serializable.";
                ReportDefect(method, Severity.High, Confidence.High, mesg);
            }

            return(Runner.CurrentRuleResult);
        }