Здравствуйте, ·, Вы писали:
·>Здравствуйте, Serginio1, Вы писали:
S>>>> [InterceptsLocation("Program.cs", line: /*L1*/, character: /*C1*/)] // refers to the call at (L1, C1) S>>·>Всё равно не понимаю смысл. Если есть исходник — Program.cs — просто патчишь его как надо в нужных строках и перекомпилируешь. S>>·>А если исходника нет, то откуда брать номера строк — неясно. S>>Зачем тебе исходник? ·>А как ещё узнать line и character?
Ну ты же в своем коде подменяешь вызов. Рослин тебе все даст.
S>>Ты заменяешь вызов в текущем коде!!! А к нему у тебя есть доступ S>>То есть вызывается не метод объекта, а расширения. S>>При этом расширение может брать данные из текущего класса. Компильнуть исходники, рефлексия, итд. ·>Почему просто сам Program.cs отредактировать нельзя?
Ну во первых неизвестно на что заменять. И вообще будет ли код заменяться. Всегда можно отменить замену и сравнивать результаты.
Ты не зависишь от имени метода.
Ну можешь моки навешать или свои логи. Вариантов много.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, ·, Вы писали:
·>Если уж патчить, то байт-код. Его хотя бы анализировать гораздо проще. ·>Ведь нужен парсер синтаксиса и семантики C#, чтобы номера строк знать и т.п.
Предпологается использовать source generator для этих целей, и в этом случае тебе на вход прилетает не голая строка, а AST от компилятора, в котором все что тебе надо уже есть, и ничего самостоятельно разбирать не надо.
S>>3. Заменить вызов в пользовательском коде: ·>Чем это принципиально отличается от "Взять текст Program.cs", "Сделать String.Replace()", "скомпилировать"?
Потому что string.Replace не в курсе семантики кода, в отличие от.
·>А какая разница?.. хоть руками, хоть "/usr/bin/patch" хоть, sed-скрипт или ещё чего. В любом случае почти неюзабельно. Если уж патчить, то байт-код. Его хотя бы анализировать гораздо проще.
Хм. А вы пробовали анализировать байткод и С#? По моему опыту, анализировать исходник проще, чем восстанавливать семантику по байт-коду.
Если у вас есть другой опыт — расскажите, как вы будете находить места вроде "в Enumerable.Where передаётся замыкание-лямбда", и как вы будете генерировать байткод для енумератора со встроенным вызовом байткода этой лямбды. ·>Ведь нужен парсер синтаксиса и семантики C#, чтобы номера строк знать и т.п.
Конечно нужен. Хорошая новость — в том, что он уже есть. Поставляется из коробки. S>>3. Заменить вызов в пользовательском коде: ·>Чем это принципиально отличается от "Взять текст Program.cs", "Сделать String.Replace()", "скомпилировать"?
Я вроде бы привёл пример кода. Какой именно вы предлагаете "Сделать String.Replace()" для достижения предложенного эффекта?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, ·, Вы писали:
·>Проблема с оптимизациями в том, что они не универсальные.
Смотря какие. ·>Машина задолбается искать места похожие на то, что можно оптимизировать и решать, стоит оптимизация того или нет и не сломается ли что.
Нет конечно. Машина железная, она не умеет задалбываться. Главное — следить, чтобы NP-полнота не вылезала.
·>В итоге вместо того, чтобы просто отредактировать Program.cs ты будешь ипаться с машиной, чтобы заставить её отредактировать Program.cs как надо.
Отчегож? Вариант "просто отредактировать Program.cs" у вас никто не отберёт — точно также, как и право писать без Linq и енумераторов.
И обязанность ипаться тоже не наложит — ведь ещё вчера вас устраивал код, в котором никакие вызовы ни на что не заменяются. Так и тут — подключите пакедж, он сколько-то мест в вашей программе прооптимизирует. Вы запустите бенчмарки, и решите — а стоит ли оно того или нет. Если не стоит — отключите пакедж и поедете дальше.
Стоимость эксперимента — на порядки ниже любой попытки пооптимизировать код вручную. ·>А если оптимизации относительно просты, то этим уже должен заниматься компилятор. Я вполне уверен, что "код соорудит объект-делегат... косвенный вызов" — давно под силу компилятору понять, что такое можно инлайнить.
Ну, так именно об этом речь и идёт. Просто желающих поулучшать компилятор — очень много. Часть из этих улучшений противоречат друг другу; значительная часть применима только к каким-то отдельным проектам (и тащить их в общий компилятор незачем). Обсуждаемая фича — возможность желающим расширять компилятор предсказуемым образом, не портя жизнь всем остальным.
S>>Высокооптимизированный код — штука трудночитаемая, и плохоисправимая. Поэтому лучше, чтобы её порождал не человек руками, а неумолимая машина автоматом. ·>Зачем машине работать с c# — языком для человеков, неясно.
Затем, что мы, как правило, хотим улучшать компилятор C#.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>К сожалению для эффективного придется генерировать структуры-итераторы, но тогда сигнатуры не совпадут и интерсептор не сработает. S>Там же и так структуры-итераторы. Единственное, что можно тут сделать более эффективно (локально) — это убрать конструирование ненужного делегата.
public static class EnumerableHelper
{
public static IEnumerable<int> Where2314234235569567(this IEnumerable<int> source, Func<int, bool> _)
{
foreach(var n in source)
if(n=>n%2==0)
yield n;
}
}
Выделенное даст боксинг при вызове. Чтобы не было боксинга в цепочке вызовов надо чтобы возвращаемое значение тоже было структурой.
Здравствуйте, ·, Вы писали:
·>Здравствуйте, Sinclair, Вы писали:
S>>Вы имеете в виду — "патчишь руками"? S>>Ну так там как раз смысл в том, чтобы патчить не руками, а автоматом. ·>А какая разница?.. хоть руками, хоть "/usr/bin/patch" хоть, sed-скрипт или ещё чего.
Разница в том, что интерсепторы патчат во время компиляции, а не на уровне файлов в репе.
·>В любом случае почти неюзабельно
Почему?
·>Если уж патчить, то байт-код. Его хотя бы анализировать гораздо проще. ·>Ведь нужен парсер синтаксиса и семантики C#, чтобы номера строк знать и т.п.
В том то и прикол, что парсер синаксиса и семантики C# есть, а готового и достаточно легковесного анализатора IL — нет.
S>>3. Заменить вызов в пользовательском коде: ·>Чем это принципиально отличается от "Взять текст Program.cs", "Сделать String.Replace()", "скомпилировать"?
Типы проверяются
Здравствуйте, rameel, Вы писали:
R>·>Если уж патчить, то байт-код. Его хотя бы анализировать гораздо проще. R>·>Ведь нужен парсер синтаксиса и семантики C#, чтобы номера строк знать и т.п. R>Предпологается использовать source generator для этих целей, и в этом случае тебе на вход прилетает не голая строка, а AST от компилятора, в котором все что тебе надо уже есть, и ничего самостоятельно разбирать не надо.
Ну так в AST напрямую и поменять один вызов метода на другой. Зачем вместо этого генерить ещё один код с инструкцией патчинга данного кода?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Ну так в AST напрямую и поменять один вызов метода на другой. Зачем вместо этого генерить ещё один код с инструкцией патчинга данного кода?
Компилятор не дает менять ast в процессе
Здравствуйте, Sinclair, Вы писали:
S>·>А какая разница?.. хоть руками, хоть "/usr/bin/patch" хоть, sed-скрипт или ещё чего. В любом случае почти неюзабельно. Если уж патчить, то байт-код. Его хотя бы анализировать гораздо проще. S>Хм. А вы пробовали анализировать байткод и С#? По моему опыту, анализировать исходник проще, чем восстанавливать семантику по байт-коду. S>Если у вас есть другой опыт — расскажите, как вы будете находить места вроде "в Enumerable.Where передаётся замыкание-лямбда", и как вы будете генерировать байткод для енумератора со встроенным вызовом байткода этой лямбды.
Ок. На самом деле на входе не Program.cs, а его AST. Ну тогда байткод, конечно сложнее будет анализировать (ибо он даже к яп не привязан). Впрочем подменить вызов метода на какой-то другой — довольно просто и в байткоде.
Тут будет вопрос как можно будет анализировать и преобразовывать аргументы вызова метода. Отличить простую лямду от непростой и что потом с ней делать. Но это всё нерелевантно. Тут вопрос не в том как можно анализировать и генерировать код, а накой результат всего этого описывать в виде патча файл/строка/позиция, и т.п., притом только с возможностью делать подмену вызова метода, вместо того, чтобы просто получить произвольный результирующий код и его компилировать как обычно.
S>>>3. Заменить вызов в пользовательском коде: S>·>Чем это принципиально отличается от "Взять текст Program.cs", "Сделать String.Replace()", "скомпилировать"? S>Я вроде бы привёл пример кода. Какой именно вы предлагаете "Сделать String.Replace()" для достижения предложенного эффекта?
Ну раз есть AST, то "распарсить Program.cs а AST", "заменить в AST вызов source.Where(n=>n%2==0) на вызов EnumerableHelper.Where2314234235569567(source)", "сохранить в Program_modified.cs вместе с реализацией Where2314234235569567 как приватного метода", "скомпилировать".
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Serginio1, Вы писали:
IT>>А можно в подменённом коде вызывать подменяемый? Если да, то имеет смысл для всяко разных аспектов. S>Там подменяешь существующий вызов. Есть сторонняя сборка, и надо подменить вызов на свой в каких то случаях например через расширения.
С этими примерами я разобрался. Вопрос был можно ли в коде, которым подменяются вызовы вызвать то, что подменяется. gandjustas ответил уже, что принципиальных проблем нет. Мне тоже так кажется. Нужно будет попробовать.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, gandjustas, Вы писали:
G>·>А какая разница?.. хоть руками, хоть "/usr/bin/patch" хоть, sed-скрипт или ещё чего. G>Разница в том, что интерсепторы патчат во время компиляции, а не на уровне файлов в репе.
Результат патчинга складывается в /tmp и туда смотрит IDE и компайлер.
G>·>В любом случае почти неюзабельно G>Почему?
Имхо очень сложно. Если где что поломается, очень сложно понять где и как. Слишком много подвижных частей.
G>·>Если уж патчить, то байт-код. Его хотя бы анализировать гораздо проще. G>·>Ведь нужен парсер синтаксиса и семантики C#, чтобы номера строк знать и т.п. G>В том то и прикол, что парсер синаксиса и семантики C# есть, а готового и достаточно легковесного анализатора IL — нет.
А, ну так эту проблему и надо решать.
S>>>3. Заменить вызов в пользовательском коде: G>·>Чем это принципиально отличается от "Взять текст Program.cs", "Сделать String.Replace()", "скомпилировать"? G>Типы проверяются
Этап "скомпилировать" тоже проверяет типы, внезапно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, gandjustas, Вы писали:
G>·>Ну так в AST напрямую и поменять один вызов метода на другой. Зачем вместо этого генерить ещё один код с инструкцией патчинга данного кода? G>Компилятор не дает менять ast в процессе
Ну это понятно, иммутабельность и всё такое. Я имею в виду генерить новый AST копируя старый с нужными изменениями.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, gandjustas, Вы писали:
G>Выделенное даст боксинг при вызове. Чтобы не было боксинга в цепочке вызовов надо чтобы возвращаемое значение тоже было структурой.
Боксинг чего?
У нас по условию был метод
Тут source — уже IEnumerable<int>. Поэтому никакого боксинга в месте вызова Where2314234235569567 не произойдёт.
Вы, наверное, имеете в виду какой-то другой сценарий. Но у меня не хватает телепатии представить, какой именно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
R>·>Ну это понятно, иммутабельность и всё такое. Я имею в виду генерить новый AST копируя старый с нужными изменениями.
R>К сожалению, C# такое не разрешает пока
Ну как раз можно создав новый метод, и заменив вызов старого на новый метод
и солнце б утром не вставало, когда бы не было меня
S>> Ну как раз можно создав новый метод, и заменив вызов старого на новый метод
R>Это можно конечно, может даже и поможет в некоторых случаях, но в крайних может потенциально привести к дублированию всех методов в проекте
Как генератор напишешь. Всё в твоих руках!
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, ·, Вы писали:
·>Ок. На самом деле на входе не Program.cs, а его AST. Ну тогда байткод, конечно сложнее будет анализировать (ибо он даже к яп не привязан). Впрочем подменить вызов метода на какой-то другой — довольно просто и в байткоде.
Это-то как раз да, но для начала нужно этот новый метод откуда-то взять. И вот получить его из AST на порядок проще, чем синтезировать из байт-кода. Я вот в своём проекте начинал как раз со сшивки байт-кода, но быстро упёрся в неразрешимые проблемы. А с AST всё получилось как надо. ·>Тут будет вопрос как можно будет анализировать и преобразовывать аргументы вызова метода. Отличить простую лямду от непростой и что потом с ней делать. Но это всё нерелевантно. Тут вопрос не в том как можно анализировать и генерировать код, а накой результат всего этого описывать в виде патча файл/строка/позиция, и т.п., притом только с возможностью делать подмену вызова метода, вместо того, чтобы просто получить произвольный результирующий код и его компилировать как обычно.
Да, выглядит это пока феерически криво. Я думаю, что парни пытаются нащупать способ дать ровно столько гибкости, сколько необходимо. Произвольные манипуляции AST чреваты тем, что новое AST не удастся скомпилировать в байт-код — и даже диагностику причин толком провести будет невозможно. Им же нужно сделать так, чтобы ошибки в коде "расширений компилятора" диагностировались, и ломали только расширения — а не сам компилятор.
Ну вот пока сделали вот такую залипуху — на конкретной фазе компиляции подтягиваем набор "патчей" и применяем их.
Так-то и SG тоже выглядят немножко странно — зачем нам порождать какие-то файлы, с каким-то текстом, когда мы работаем с AST? Не проще ли было бы сразу породить в памяти нужное AST и отдать компилятору?
·>Ну раз есть AST, то "распарсить Program.cs а AST", "заменить в AST вызов source.Where(n=>n%2==0) на вызов EnumerableHelper.Where2314234235569567(source)", "сохранить в Program_modified.cs вместе с реализацией Where2314234235569567 как приватного метода", "скомпилировать".
Ну, это примерно то же самое, вид сбоку. Только придётся делать плюс-минус каждый раз, как происходит компиляция. А SG и Interceptors срабатывают однократно. Ну, и в вашем предложении получается удвоение объёма кода на ровном месте — ведь придётся хранить полный клон всего Program.cs, который отличается только одной строчкой.
А в нынешнем подходе хранится "набор патчей" в виде списка атрибутов InterceptsLocation.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
S>Так-то и SG тоже выглядят немножко странно — зачем нам порождать какие-то файлы, с каким-то текстом, когда мы работаем с AST? Не проще ли было бы сразу породить в памяти нужное AST и отдать компилятору?
Ну из рослингского аст ты легко получишь код. Зато интеллисенс будет работать.
и солнце б утром не вставало, когда бы не было меня