Здравствуйте, Sinclair, Вы писали:
S>·>Ок. На самом деле на входе не Program.cs, а его AST. Ну тогда байткод, конечно сложнее будет анализировать (ибо он даже к яп не привязан). Впрочем подменить вызов метода на какой-то другой — довольно просто и в байткоде. S>Это-то как раз да, но для начала нужно этот новый метод откуда-то взять. И вот получить его из AST на порядок проще, чем синтезировать из байт-кода. Я вот в своём проекте начинал как раз со сшивки байт-кода, но быстро упёрся в неразрешимые проблемы. А с AST всё получилось как надо.
Просто меня немного удивляет сама идея — мы пишем на ЯП вроде как высокого уровня — но получается хрень, что приходится потом подправлять. Ну ведь ЯП же высокого уровня — значит можно писать же нормально сразу?
А манипуляции с байткодом вроде как более менее понятны — именно какие-то низкоуровневые штуки, инструментация, типа обернуть что-нибудь как-нибудь с целью логгирования или типа того, AOP какой-нибудь.
Править же код на c# — это кажется дикостью. c# пишут человеки для человеков в первую очередь. Если на нём нельзя написать нормально что тебе хочется — есть другие ЯП.
S>·>Тут будет вопрос как можно будет анализировать и преобразовывать аргументы вызова метода. Отличить простую лямду от непростой и что потом с ней делать. Но это всё нерелевантно. Тут вопрос не в том как можно анализировать и генерировать код, а накой результат всего этого описывать в виде патча файл/строка/позиция, и т.п., притом только с возможностью делать подмену вызова метода, вместо того, чтобы просто получить произвольный результирующий код и его компилировать как обычно. S>Да, выглядит это пока феерически криво. Я думаю, что парни пытаются нащупать способ дать ровно столько гибкости, сколько необходимо. Произвольные манипуляции AST чреваты тем, что новое AST не удастся скомпилировать в байт-код — и даже диагностику причин толком провести будет невозможно. Им же нужно сделать так, чтобы ошибки в коде "расширений компилятора" диагностировались, и ломали только расширения — а не сам компилятор.
По-моему это норма — такие манипуляции — это как крайнее средство. Байткод, кстати, верифицируется. Если нагенерится фигня — оно тупо не загрузится.
S>Ну вот пока сделали вот такую залипуху — на конкретной фазе компиляции подтягиваем набор "патчей" и применяем их. S>Так-то и SG тоже выглядят немножко странно — зачем нам порождать какие-то файлы, с каким-то текстом, когда мы работаем с AST? Не проще ли было бы сразу породить в памяти нужное AST и отдать компилятору?
Именно. Фактически с этого вопроса я и влез в этот топик.
Сам SG, т.е. программно сгеренить исходник — это ещё ок, т.к. потом этот исходник можно показать человеку, чтобы он видел что происходит, бряку всунуть, дебагером пройтись, плюс вся навигация в IDE, стектрейсы и т.п.
Сабжевое же указание номеров строк в другом файле — это хрень полная, т.к. нечеловекочитаемо, непонятна целевая аудитория. Выглядит как workaround проблем дизайна их тулчейна.
S>·>Ну раз есть AST, то "распарсить Program.cs а AST", "заменить в AST вызов source.Where(n=>n%2==0) на вызов EnumerableHelper.Where2314234235569567(source)", "сохранить в Program_modified.cs вместе с реализацией Where2314234235569567 как приватного метода", "скомпилировать". S>Ну, это примерно то же самое, вид сбоку. Только придётся делать плюс-минус каждый раз, как происходит компиляция.
Да ровно так же — что генераторы, что замены надо делать каждый раз при изменении Program.cs и/или самого генератора|заменятора.
S>А SG и Interceptors срабатывают однократно.
Ага-ага. Кеширование промежуточных резлультатов умел ещё make, которому скоро пол века стукнет.
S>Ну, и в вашем предложении получается удвоение объёма кода на ровном месте — ведь придётся хранить полный клон всего Program.cs, который отличается только одной строчкой. S>А в нынешнем подходе хранится "набор патчей" в виде списка атрибутов InterceptsLocation.
так как это .cs — это должно быть для человеков. И в этом случае экономия на байтах не оправдана.
Лично я не смогу читать оригинальный Program.cs и в мысленно накладывать патчи, описанные в других файлах. Может, конечно, это я только такой отсталый, но, имхо, позволять машине так издеваться над человеками — негуманно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
·>Лично я не смогу читать оригинальный Program.cs и в мысленно накладывать патчи, описанные в других файлах. Может, конечно, это я только такой отсталый, но, имхо, позволять машине так издеваться над человеками — негуманно.
Ты тот же Linq читаешь с кучей расширений. И ничего. Главное результат. Ну и можешь помечать комментарием (атрибутами) код который должен подменяться. Это и генератору хорошо и тебе при чтении
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>·>Лично я не смогу читать оригинальный Program.cs и в мысленно накладывать патчи, описанные в других файлах. Может, конечно, это я только такой отсталый, но, имхо, позволять машине так издеваться над человеками — негуманно. S> Ты тот же Linq читаешь с кучей расширений. И ничего. Главное результат. Ну и можешь помечать комментарием (атрибутами) код который должен подменяться. Это и генератору хорошо и тебе при чтении
Ты вообще за контекстом не следишь? Какое это имеет отношение к InterceptsLocation?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Здравствуйте, Serginio1, Вы писали:
S>>·>Лично я не смогу читать оригинальный Program.cs и в мысленно накладывать патчи, описанные в других файлах. Может, конечно, это я только такой отсталый, но, имхо, позволять машине так издеваться над человеками — негуманно. S>> Ты тот же Linq читаешь с кучей расширений. И ничего. Главное результат. Ну и можешь помечать комментарием (атрибутами) код который должен подменяться. Это и генератору хорошо и тебе при чтении ·>Ты вообще за контекстом не следишь? Какое это имеет отношение к InterceptsLocation?
Прямое. Ты можешь пометить заменяемый код как комментарий. Это помощь и SG для создания расширения и атрибутов InterceptsLocation и тебе для чтения
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Silver_S, Вы писали:
S>>interceptors понравились для SG. S>> Теперь и существующий код можно подменять! S>>https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md
S_S>Немного странноватая фича. Выглядит как инструкция — в теле метода в такой-то строке подменить вызов метода. Для каждого вида изменений в коде по отдельной такой фиче что-ли делать будут. Не проще ли было разрешить в SG подменять все тело метода (все определение).
Перехват осуществляется посредством указания атрибута InterceptsLocation, в который надо передать имя файла и позиции строки и символа, на которых вызывается метод.
Хоть польза для AOT здесь также имеется, фокус приходится на кодогенерацию. Например, можно было бы помечтать о библиотеках, упрощающих работу с аспектно-ориентированным программированием. Однако ещё более заманчиво звучат фреймворки для юнит-тестов – наконец-то можно будет перестать делать по интерфейсу на каждый класс, просто чтобы замокать его в тестах. По крайней мере, это активно дискутируется в сообществе, что приятно.
В любом случае, генераторы кода оказались невероятно мощным инструментом, так что расширение их функционала не может не радовать.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>>> Ты тот же Linq читаешь с кучей расширений. И ничего. Главное результат. Ну и можешь помечать комментарием (атрибутами) код который должен подменяться. Это и генератору хорошо и тебе при чтении S>·>Ты вообще за контекстом не следишь? Какое это имеет отношение к InterceptsLocation? S> Прямое. Ты можешь пометить заменяемый код как комментарий. Это помощь и SG для создания расширения и атрибутов InterceptsLocation и тебе для чтения
Но ведь можешь-то и не пометить! Отмечать в исходнике части кода которые надо как-то особо обработать, через partial/спец-аннотации — хороший подход. А вот InterceptsLocation — жуть полная.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Здравствуйте, Serginio1, Вы писали:
S>>>> Ты тот же Linq читаешь с кучей расширений. И ничего. Главное результат. Ну и можешь помечать комментарием (атрибутами) код который должен подменяться. Это и генератору хорошо и тебе при чтении S>>·>Ты вообще за контекстом не следишь? Какое это имеет отношение к InterceptsLocation? S>> Прямое. Ты можешь пометить заменяемый код как комментарий. Это помощь и SG для создания расширения и атрибутов InterceptsLocation и тебе для чтения ·>Но ведь можешь-то и не пометить! Отмечать в исходнике части кода которые надо как-то особо обработать, через partial/спец-аннотации — хороший подход. А вот InterceptsLocation — жуть полная.
Перехват осуществляется посредством указания атрибута InterceptsLocation, в который надо передать имя файла и позиции строки и символа, на которых вызывается метод.
Хоть польза для AOT здесь также имеется, фокус приходится на кодогенерацию. Например, можно было бы помечтать о библиотеках, упрощающих работу с аспектно-ориентированным программированием. Однако ещё более заманчиво звучат фреймворки для юнит-тестов – наконец-то можно будет перестать делать по интерфейсу на каждый класс, просто чтобы замокать его в тестах. По крайней мере, это активно дискутируется в сообществе, что приятно.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>наконец-то можно будет перестать делать по интерфейсу на каждый класс, просто чтобы замокать его в тестах.
Вот собственно реальная проблема — так ведь её и нужно фиксить. Достаточно просто позволить мокать классы.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
S>>наконец-то можно будет перестать делать по интерфейсу на каждый класс, просто чтобы замокать его в тестах. ·>Вот собственно реальная проблема — так ведь её и нужно фиксить. Достаточно просто позволить мокать классы.
InterceptsLocation самое простое решение. Для моков вообще не трогает код.
Куда проще?
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>>>наконец-то можно будет перестать делать по интерфейсу на каждый класс, просто чтобы замокать его в тестах. S>·>Вот собственно реальная проблема — так ведь её и нужно фиксить. Достаточно просто позволить мокать классы. S> InterceptsLocation самое простое решение. Для моков вообще не трогает код. S>Куда проще?
Ага, простое, но неправильное. Т.к. ты сможешь мокать только классы написанные на c# и только лежащие в твоём проекте в виде исходников.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
S>>>>наконец-то можно будет перестать делать по интерфейсу на каждый класс, просто чтобы замокать его в тестах. S>>·>Вот собственно реальная проблема — так ведь её и нужно фиксить. Достаточно просто позволить мокать классы. S>> InterceptsLocation самое простое решение. Для моков вообще не трогает код. S>>Куда проще? ·>Ага, простое, но неправильное. Т.к. ты сможешь мокать только классы написанные на c# и только лежащие в твоём проекте в виде исходников.
Я могу мокать вызовы любых классов!
У меня есть проект я хочу проверить работу некоего класса, но вызовы определенных классов, я хочу поменить на свои.
При этом сам объект этого класса может быть null. Не суть. Главное подменить вызов!
Эти классы могут быть написаны на чем угодно. Подменяются вызовы в моем коде!
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>·>Ага, простое, но неправильное. Т.к. ты сможешь мокать только классы написанные на c# и только лежащие в твоём проекте в виде исходников. S>Эти классы могут быть написаны на чем угодно. Подменяются вызовы в моем коде!
Именно, что _только_ в твоём (притом если ты _только_ на шарпе пишешь). Подменять надо не место вызова, а то что вызывается. Т.к место вызова может быть где угодно, и в библиотечном коде в том числе.
Простой пример. В своём коде ты пишешь:
void myMethod(MockableThing thing)
{
Logger.LogInformation("thing is {thing}", thing);
}
где-то в недрах какой-то библиотеки логгирования позовётся ToString. Как InterceptsLocation поможет тебе замокать этот вызов?
Этой же проблеме будет подвержена и работа с аспектно-ориентированным программированием.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Здравствуйте, Serginio1, Вы писали:
S>>·>Ага, простое, но неправильное. Т.к. ты сможешь мокать только классы написанные на c# и только лежащие в твоём проекте в виде исходников. S>>Эти классы могут быть написаны на чем угодно. Подменяются вызовы в моем коде! ·>Именно, что _только_ в твоём (притом если ты _только_ на шарпе пишешь). Подменять надо не место вызова, а то что вызывается. Т.к место вызова может быть где угодно, и в библиотечном коде в том числе. ·>Простой пример. В своём коде ты пишешь: ·>
·>где-то в недрах какой-то библиотеки логгирования позовётся ToString. Как InterceptsLocation поможет тебе замокать этот вызов? ·>Этой же проблеме будет подвержена и работа с аспектно-ориентированным программированием.
Вот я Logger.LogInformation("thing is {thing}", thing); я и замокаю.
Суть моков проверить свой код, а не сторонних библиотек. То есть я должен подсунуть свой ответ и проверить различные вариации для проверки своего кода.
Ты разве моками не занимался?
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>Вот я Logger.LogInformation("thing is {thing}", thing); я и замокаю.
В смысле будешь выпиливать все логгинг стейтменты при прогоне тестов? Для упавшего теста ты просто не сможешь разобраться почему он упал, если это произошло на билд-сервере вчера ночью. Особенно актуально для какого-нибудь хитрого многопоточного кода.
S>Суть моков проверить свой код, а не сторонних библиотек. То есть я должен подсунуть свой ответ и проверить различные вариации для проверки своего кода.
Но если ты в своём коде используешь хоть какой-нибудь библиотечный код, то всё.
S>Ты разве моками не занимался?
Занимался конечно. Надо создавать моковый инстанс thing, и тогда он может гулять по всему коду где угодно. А мокать call-sites — бесполезно, работает только на игрушечных примерах.
В java такой проблемы с моками никогда не было. Стандартно там можно создавать моки для любого класса, который не final (или sealed в терминологии c#). Впрочем, есть ещё PowerMock — она манипуляцией байткода позволяет мокать вообще всё, но это считается bad practice.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Здравствуйте, Serginio1, Вы писали:
S>>Вот я Logger.LogInformation("thing is {thing}", thing); я и замокаю. ·>В смысле будешь выпиливать все логгинг стейтменты при прогоне тестов? Для упавшего теста ты просто не сможешь разобраться почему он упал, если это произошло на билд-сервере вчера ночью. Особенно актуально для какого-нибудь хитрого многопоточного кода.
S>>Суть моков проверить свой код, а не сторонних библиотек. То есть я должен подсунуть свой ответ и проверить различные вариации для проверки своего кода. ·>Но если ты в своём коде используешь хоть какой-нибудь библиотечный код, то всё.
S>>Ты разве моками не занимался? ·>Занимался конечно. Надо создавать моковый инстанс thing, и тогда он может гулять по всему коду где угодно. А мокать call-sites — бесполезно, работает только на игрушечных примерах. ·>В java такой проблемы с моками никогда не было. Стандартно там можно создавать моки для любого класса, который не final (или sealed в терминологии c#). Впрочем, есть ещё PowerMock — она манипуляцией байткода позволяет мокать вообще всё, но это считается bad practice.
Во во создавать кучу интерфейсов и их реализацию. Про это и речь
Еще раз меня не интересуют сторонний код. Меня интересует только свой.
У меня может не быть возможности использовать сторонний код либо по времени исполнения, либо конкретно нельзя подключиться, либо долгая инициализация, а мне надо проверить только конкретный кусок кода итд.
Вот InterceptsLocation как раз для этого прекрасно подходят!
В статье же прямо написано
Хоть польза для AOT здесь также имеется, фокус приходится на кодогенерацию. Например, можно было бы помечтать о библиотеках, упрощающих работу с аспектно-ориентированным программированием. Однако ещё более заманчиво звучат фреймворки для юнит-тестов – наконец-то можно будет перестать делать по интерфейсу на каждый класс, просто чтобы замокать его в тестах. По крайней мере, это активно дискутируется в сообществе, что приятно.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Sinclair, Вы писали:
S>Так-то и SG тоже выглядят немножко странно — зачем нам порождать какие-то файлы, с каким-то текстом, когда мы работаем с AST? Не проще ли было бы сразу породить в памяти нужное AST и отдать компилятору?
Разве что в виде квази-цитирования. Вручную его задолбаешься создавать.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, ·, Вы писали:
·>Сабжевое же указание номеров строк в другом файле — это хрень полная, т.к. нечеловекочитаемо, непонятна целевая аудитория. Выглядит как workaround проблем дизайна их тулчейна.
Сгенерируй рядом комментарий с подробностями специально для человеков. В чём проблема?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>·>Сабжевое же указание номеров строк в другом файле — это хрень полная, т.к. нечеловекочитаемо, непонятна целевая аудитория. Выглядит как workaround проблем дизайна их тулчейна. IT>Сгенерируй рядом комментарий с подробностями специально для человеков. В чём проблема?
Рядом где? Генерировать можно только генерируемый код. Исходный Program.cs менять нельзя, тут выше писали.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Serginio1, Вы писали:
S>>>Ты разве моками не занимался? S>·>Занимался конечно. Надо создавать моковый инстанс thing, и тогда он может гулять по всему коду где угодно. А мокать call-sites — бесполезно, работает только на игрушечных примерах. S>·>В java такой проблемы с моками никогда не было. Стандартно там можно создавать моки для любого класса, который не final (или sealed в терминологии c#). Впрочем, есть ещё PowerMock — она манипуляцией байткода позволяет мокать вообще всё, но это считается bad practice. S>Во во создавать кучу интерфейсов и их реализацию. Про это и речь
Не понял ты о чём. В java никогда не надо было создавать кучу интерфейсов и их реализацию.
S> Еще раз меня не интересуют сторонний код. Меня интересует только свой.
Твой код может обращаться к стороннему, к каким-нибудь утилитным функциям.
S>У меня может не быть возможности использовать сторонний код либо по времени исполнения, либо конкретно нельзя подключиться, либо долгая инициализация, а мне надо проверить только конкретный кусок кода итд. S> Вот InterceptsLocation как раз для этого прекрасно подходят!
Только в весьма ограниченных случаях.
S>В статье же прямо написано
На заборе тоже написано.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Причем тут ява? S>> Еще раз меня не интересуют сторонний код. Меня интересует только свой. ·>Твой код может обращаться к стороннему, к каким-нибудь утилитным функциям.
Вот эти вызовы мне и надо подменить через InterceptsLocation и вернуть нужные мне данные изменить состояние моих объектов! S>>У меня может не быть возможности использовать сторонний код либо по времени исполнения, либо конкретно нельзя подключиться, либо долгая инициализация, а мне надо проверить только конкретный кусок кода итд. S>> Вот InterceptsLocation как раз для этого прекрасно подходят! ·>Только в весьма ограниченных случаях.
Везде! S>>В статье же прямо написано ·>На заборе тоже написано.
Аргуиент!
и солнце б утром не вставало, когда бы не было меня