Есть ли способ найти весь код, который вызывает операции ввода-вывода, такие как File.WriteAllText
, Request.Files["filename"].SaveAs("out")
и т.д.?
На данный момент я могу просто grep для всех возможных распространенных способов чтения/записи файлов с чем-то вроде этого:
grep 'SaveAs' -I -r . -l | grep "\.cs"
Это неудовлетворительно, потому что я не могу думать о всех возможных способах чтения и записи файлов. Может быть, это может быть сделано путем отражения каким-то образом или путем анализа системных вызовов в скомпилированных двоичных файлах? Есть идеи?
EDIT: если метод A вызывает метод B и метод B вызывает метод C, а метод C выполняет операцию с файлом, было бы неплохо также идентифицировать этот код. Однако просто для упрощения проблемы было бы достаточно найти прямые вызовы для ввода-вывода.
Это зависит от того, сколько вы хотите сделать в углубленном анализе.
Если вы действительно серьезно относитесь к использованию всех вызовов метода System.IO, я бы предложил использовать NRefactory. Это внешний анализатор С#, который может анализировать код С# и генерируемое дерево синтаксиса и распознаватель кода.
Там хороший учебник об использовании его в проекте кода, который может помочь вам начать работу. Также в учебнике по проекту кода есть вспомогательные классы, которые позволяют загружать все решение и предоставлять вам код-преобразователь.
Если вам нужно больше образцов, вы можете найти их в своем блоге.
PS: Я попытаюсь продлить этот ответ с помощью образца кода за несколько часов.
Поэтому по прихоти я решил попробовать это с помощью Mono.Cecil.
Очень просто рекурсивно получить список всех методов, которые вызывает метод:
static IEnumerable<MethodDefinition> GetMethodsCalledInMethod(MethodDefinition md)
{
if (md.Body == null)
{
return Enumerable.Empty<MethodDefinition>();
}
return md.Body.Instructions
.Select(i => i.Operand)
.OfType<MethodReference>()
.Select(mr => mr.Resolve())
.Where(mr => mr != null);
}
Вы можете получить все методы в сборке, которую хотите проверить:
var ad = AssemblyDefinition.ReadAssembly(typeof(ATypeInTheAssembly).Assembly.Location);
var allMethodsInAssembly = ad.Modules
.SelectMany(m => m.Types)
.SelectMany(t => t.Methods));
Затем вы можете пройти через это дерево вызовов метода, пока не найдете вызов метода, который выглядит как вызов ввода-вывода.
Func<MethodDefinition, bool> isFileOperation = md =>
md.DeclaringType.Name == "FileStream";
Достаточно ли этого? Я не знаю. File.WriteAllText
использует FileStream
. Вы также предоставили пример SaveAs
. Но каждый доступ к файлу проходит через FileStream
? Я не могу сказать.
Этот подход также имеет проблемы:
File.WriteAllLines
я получил результат немедленно, но когда я попытался проанализировать сам анализатор, он потерялся в дереве.Если вы планируете разрабатывать этот инструмент, вы можете начать здесь.
В статье описаны способы обнаружения зависимостей сборки и метода, поэтому вы можете найти все методы, которые вызывают примитивы ввода-вывода, такие как FileStream.Write
.