Как я могу определить, какие исключения могут быть выданы данным методом?

Мне удалось изменить пример проекта Github> googlecast / CastHelloText-chrome для отображения веб-страницы.

  • В файле receiver.html я заменил DIV на IFRAME и установил его для 100% ширины и высоты.
  • Затем в функции displayText() я изменил innerHTML на src.
  • Единственной модификацией, внесенной в отправителя chromehellotext.html, было заменить YOUR_APPLICATION_ID идентификатором приложения, созданным путем регистрации получателя.

Обратите внимание , что некоторые сайты, такие как Google и Yahoo, не будут отображаться внутри iframe. Если вам действительно нужно это сделать, вы можете пропустить iframe и просто установить window.location.href на URL. Это, конечно, перезапишет код получателя, поэтому единственный способ изменить URL-адреса - это сначала вручную отключиться и перезагрузить. Вместо этого я попытался использовать window.open, но это не сработало.

32
задан Community 23 May 2017 в 12:02
поделиться

8 ответов

Следуя моему предыдущему ответу, мне удалось создать базовый искатель исключений. Он использует класс ILReader , основанный на отражении, доступный здесь в блоге Хайбо Луо MSDN. (Просто добавьте ссылку на проект.)

Обновления:

  1. Теперь обрабатывает локальные переменные и стек.
    • Правильно обнаруживает исключения, возвращаемые из вызовов методов или полей, а затем создаваемые.
    • Теперь обрабатывает выталкивания / выталкивания стека полностью и надлежащим образом.

Вот полный код. Вы просто хотите использовать метод GetAllExceptions (MethodBase) либо как расширение, либо как статический метод.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using ClrTest.Reflection;

public static class ExceptionAnalyser
{
    public static ReadOnlyCollection<Type> GetAllExceptions(this MethodBase method)
    {
        var exceptionTypes = new HashSet<Type>();
        var visitedMethods = new HashSet<MethodBase>();
        var localVars = new Type[ushort.MaxValue];
        var stack = new Stack<Type>();
        GetAllExceptions(method, exceptionTypes, visitedMethods, localVars, stack, 0);

        return exceptionTypes.ToList().AsReadOnly();
    }

    public static void GetAllExceptions(MethodBase method, HashSet<Type> exceptionTypes,
        HashSet<MethodBase> visitedMethods, Type[] localVars, Stack<Type> stack, int depth)
    {
        var ilReader = new ILReader(method);
        var allInstructions = ilReader.ToArray();

        ILInstruction instruction;
        for (int i = 0; i < allInstructions.Length; i++)
        {
            instruction = allInstructions[i];

            if (instruction is InlineMethodInstruction)
            {
                var methodInstruction = (InlineMethodInstruction)instruction;

                if (!visitedMethods.Contains(methodInstruction.Method))
                {
                    visitedMethods.Add(methodInstruction.Method);
                    GetAllExceptions(methodInstruction.Method, exceptionTypes, visitedMethods,
                        localVars, stack, depth + 1);
                }

                var curMethod = methodInstruction.Method;
                if (curMethod is ConstructorInfo)
                    stack.Push(((ConstructorInfo)curMethod).DeclaringType);
                else if (method is MethodInfo)
                    stack.Push(((MethodInfo)curMethod).ReturnParameter.ParameterType);
            }
            else if (instruction is InlineFieldInstruction)
            {
                var fieldInstruction = (InlineFieldInstruction)instruction;
                stack.Push(fieldInstruction.Field.FieldType);
            }
            else if (instruction is ShortInlineBrTargetInstruction)
            {
            }
            else if (instruction is InlineBrTargetInstruction)
            {
            }
            else
            {
                switch (instruction.OpCode.Value)
                {
                    // ld*
                    case 0x06:
                        stack.Push(localVars[0]);
                        break;
                    case 0x07:
                        stack.Push(localVars[1]);
                        break;
                    case 0x08:
                        stack.Push(localVars[2]);
                        break;
                    case 0x09:
                        stack.Push(localVars[3]);
                        break;
                    case 0x11:
                        {
                            var index = (ushort)allInstructions[i + 1].OpCode.Value;
                            stack.Push(localVars[index]);
                            break;
                        }
                    // st*
                    case 0x0A:
                        localVars[0] = stack.Pop();
                        break;
                    case 0x0B:
                        localVars[1] = stack.Pop();
                        break;
                    case 0x0C:
                        localVars[2] = stack.Pop();
                        break;
                    case 0x0D:
                        localVars[3] = stack.Pop();
                        break;
                    case 0x13:
                        {
                            var index = (ushort)allInstructions[i + 1].OpCode.Value;
                            localVars[index] = stack.Pop();
                            break;
                        }
                    // throw
                    case 0x7A:
                        if (stack.Peek() == null)
                            break;
                        if (!typeof(Exception).IsAssignableFrom(stack.Peek()))
                        {
                            //var ops = allInstructions.Select(f => f.OpCode).ToArray();
                            //break;
                        }
                        exceptionTypes.Add(stack.Pop());
                        break;
                    default:
                        switch (instruction.OpCode.StackBehaviourPop)
                        {
                            case StackBehaviour.Pop0:
                                break;
                            case StackBehaviour.Pop1:
                            case StackBehaviour.Popi:
                            case StackBehaviour.Popref:
                            case StackBehaviour.Varpop:
                                stack.Pop();
                                break;
                            case StackBehaviour.Pop1_pop1:
                            case StackBehaviour.Popi_pop1:
                            case StackBehaviour.Popi_popi:
                            case StackBehaviour.Popi_popi8:
                            case StackBehaviour.Popi_popr4:
                            case StackBehaviour.Popi_popr8:
                            case StackBehaviour.Popref_pop1:
                            case StackBehaviour.Popref_popi:
                                stack.Pop();
                                stack.Pop();
                                break;
                            case StackBehaviour.Popref_popi_pop1:
                            case StackBehaviour.Popref_popi_popi:
                            case StackBehaviour.Popref_popi_popi8:
                            case StackBehaviour.Popref_popi_popr4:
                            case StackBehaviour.Popref_popi_popr8:
                            case StackBehaviour.Popref_popi_popref:
                                stack.Pop();
                                stack.Pop();
                                stack.Pop();
                                break;
                        }

                        switch (instruction.OpCode.StackBehaviourPush)
                        {
                            case StackBehaviour.Push0:
                                break;
                            case StackBehaviour.Push1:
                            case StackBehaviour.Pushi:
                            case StackBehaviour.Pushi8:
                            case StackBehaviour.Pushr4:
                            case StackBehaviour.Pushr8:
                            case StackBehaviour.Pushref:
                            case StackBehaviour.Varpush:
                                stack.Push(null);
                                break;
                            case StackBehaviour.Push1_push1:
                                stack.Push(null);
                                stack.Push(null);
                                break;
                        }

                        break;
                }
            }
        }
    }
}

Подводя итог, этот алгоритм рекурсивно перечисляет (сначала в глубину) любые методы, вызываемые в указанном, путем чтения инструкции CIL (а также отслеживание уже посещенных методов). Он поддерживает единый список коллекций, которые могут быть созданы с помощью объекта HashSet , который возвращается в конце. Кроме того, он поддерживает массив локальных переменных и стек, чтобы отслеживать исключения, которые не генерируются сразу после их создания.

Конечно, этот код в нем не является безошибочным » s текущее состояние. Есть несколько улучшений, которые мне нужно сделать, чтобы он был надежным, а именно:

  1. Обнаружение исключений, которые не генерируются напрямую, с помощью конструктора исключений. (т.е. исключение извлекается из локальной переменной или вызова метода.)
  2. Исключения поддержки извлекаются из стека, а затем возвращаются обратно.
  3. Добавить обнаружение управления потоком. Блоки try-catch, обрабатывающие любое возникшее исключение, должны удалять соответствующее исключение из списка, если только не обнаружена инструкция rethrow .

Кроме того, я считаю, что код разумно полный. Может потребоваться немного больше исследований, прежде чем я точно выясню, как выполнять обнаружение управления потоком (хотя я считаю, что теперь могу увидеть, как это работает на уровне IL).

46
ответ дан 27 November 2019 в 20:17
поделиться

Моя методология для этого типа ситуаций - обрабатывать все исключения, которые я хочу, а затем переопределять событие UnhandledException приложения для регистрации любых других, о которых я не знаю. Затем, если я столкнусь с чем-то, что, как мне кажется, я могу решить, я просто обновлю его соответствующим образом.

Надеюсь, что это поможет!

2
ответ дан 27 November 2019 в 20:17
поделиться

This answer was posted in the other question you reference and I know I have recommended it before in another similar question. You should give Exception Hunter a try. It lists out every single exception that can possibly be thrown. When I ran it for the first time on my code I was quite surprised by the size of this list for even simple functions. There is a 30 day trial for free so there is no reason not to give it a try.

9
ответ дан 27 November 2019 в 20:17
поделиться

Это не должно быть очень сложно. Вы можете получить список исключений, созданных следующим способом:

IEnumerable<TypeReference> GetCreatedExceptions(MethodDefinition method)
{
    return method.GetInstructions()
        .Where(i => i.OpCode == OpCodes.Newobj)
        .Select(i => ((MemberReference) i.Operand).DeclaringType)
        .Where(tr => tr.Name.EndsWith("Exception"))
        .Distinct();
}

Фрагменты используют Lokad.Quality.dll из Open Source Lokad Shared Libraries (которые используют Mono.Cecil для выполнения тяжелой работы вокруг кода отражение). На самом деле я поместил этот код в один из тестовых примеров в магистрали .

Скажем, у нас есть такой класс:

class ExceptionClass
{
    public void Run()
    {
        InnerCall();
        throw new NotSupportedException();
    }

    void InnerCall()
    {
        throw new NotImplementedException();
    }
}

затем, чтобы получить все исключения только из метода Run:

var codebase = new Codebase("Lokad.Quality.Test.dll");
var type = codebase.Find<ExceptionClass>();
var method = type.GetMethods().First(md => md.Name == "Run");

var exceptions = GetCreatedExceptions(method)
    .ToArray();

Assert.AreEqual(1, exceptions.Length);
Assert.AreEqual("NotSupportedException", exceptions[0].Name);

Теперь все, что осталось, - это пройти вниз по стеку вызовов методов до определенной глубины. Вы можете получить список методов, на которые ссылается такой метод:

var references = method.GetReferencedMethods();

Теперь, прежде чем мы сможем вызвать GetCreatedExceptions для любого метода в стеке, нам просто нужно фактически заглянуть в кодовую базу и разрешить все экземпляры MethodReference экземплярам MethodDefinition, фактически содержащим байтовый код (с некоторым кешированием для избегайте сканирования существующих веток). Это наиболее трудоемкая часть кода (поскольку объект Codebase не реализует поиск методов поверх Cecil), но это должно быть выполнимо.

12
ответ дан 27 November 2019 в 20:17
поделиться

I'm very doubtful there is any (at least straightforward) way to do this in C#. Saying that, I do have an idea that may work, so read on please...

Firstly, it's worth noting that doing a brute-force search using a huge number of permutations of arguments clearly is not feasible. Even having prior knowledge of parameter types (which I don't believe is desirable in your situation), the task essentially reduces to the halting problem in the general case, since you don't know the function will terminate given certian parameters. Ideally, exceptions should stop this, but it isn't always the case of course.

Now, perhaps the most reliable method is to analyse the source code (or more realistically CIL code) itself to see which exceptions might be thrown. This I believe may actually be workable. A simple algorithm might go something like:

  1. Do a depth-first or breadth-first search of the given method. Find all methods/properties that are called anywhere within the method body, and recurse on them.
  2. For each block of CIL code in tree of methods/propreties, examine code for any exceptions that might be thrown, and add to a list.

This would even allow you to get detailed information about the exceptions, such as whether they are thrown directly by the called method, or deeper in the call stack, or even the exception messages themselves. Anyway, I'll consider giving an implementation of this a try later this afternoon, so I'll let you know how feasible the idea is then.

2
ответ дан 27 November 2019 в 20:17
поделиться

Unlike java C# does not have the concept of checked exceptions.

On a macro level you should catch everything and log or inform the user of the error. When you know about the specific exceptions a method may raise then definetly handle that appropriately but be sure to let any other exceptions bubble up (preferably) or log them, otherwise you will have bugs that you will not be able to find and generally make life miserable for anyone hired to help reduce the bug list - have been there, not fun! :)

1
ответ дан 27 November 2019 в 20:17
поделиться

John Robbins had a series of articles on creating FxCop rules including an MSDN article that would indicate which exceptions were thrown. This was to warn about missing XML documentation for the exceptions but the idea would be the same.

1
ответ дан 27 November 2019 в 20:17
поделиться

Я написал надстройку для Reflector под названием ExceptionFinder, которая этим занимается. Вы можете получить его по адресу:

http://exfinderreflector.codeplex.com/

С уважением, Джейсон

1
ответ дан 27 November 2019 в 20:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: