Могу ли я переопределить методы Ruby, написанные на C? - PullRequest
8 голосов
/ 21 августа 2011

Можно ли переопределить методы, являющиеся частью самого Ruby, такие как rb_error_frozen, написанные на C, с кодом Ruby?

Background : I 'Интересно, можно ли заставить Ruby просто регистрировать предупреждение, а не вызывать исключение, когда замороженный объект изменяется.Таким образом, я могу регистрировать различные изменения состояния, а не останавливаться, когда происходит первая.

Я в первую очередь думаю сделать это с YARV, но я мог бы использовать другую реализацию, если бы это облегчало.

И да, это приветственный проект!Не пытайтесь сделать это в производственной среде!

Ответы [ 2 ]

5 голосов
/ 21 августа 2011

Я могу говорить только за МРТ / ЯРВ, но я попробую.Вы можете переопределить функции, которые происходят из C в Ruby, если функция C была явно определена как метод объекта Ruby.Например, Kernel#extend явно определено в C как

rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);

Так как функция C rb_obj_extend была «связана» (в кавычках, потому что я, образно говоря, я не имею в виду Cздесь) с методом Kernel#extend в мире Ruby, теоретически вы можете переопределить поведение rb_obj_extend, если переопределите Kernel#extend.

Я бы сказал, учитывая следующие два условия, которые вы могли бы утверждать, что вы на самом деле «отвергли» функцию rb_ * C:

  • функция rb_ * C была «связана» с некоторымиОбъект Ruby, так что у нас есть дескриптор в мире Ruby, который служит ловушкой, которую мы можем переопределить
  • , данный метод rb_ * используется только в этом одном месте именно для этой цели, он повторно используется нигде больше

Теперь, если вы посмотрите на rb_error_frozen, он не удовлетворяет ни одному из этих двух условий.Это помощник в реализации C, то есть он вызывается из нескольких мест.И он не был явно «связан» ни с одним объектом Ruby, поэтому у вас нет ловушки, где вы могли бы переопределить его.

Однако не все потеряно.Вы не можете напрямую переопределить rb_error_frozen, но вы все равно можете попробовать переопределить все методы Ruby, где rb_error_frozen всплывает до "поверхности Ruby".Под этим я подразумеваю, что вы можете проверить все места в источниках C, где используется rb_error_frozen, и из этих мест попытаться найти каждый метод Ruby, который может вызвать эти биты кода.Если это закрытый набор, вы можете просто переопределить все эти методы, чтобы «де-факто-переопределить» поведение rb_error_frozen.

Однако это только лоскутное решение.Вся ваша тяжелая работа потеряна, если кто-то решит написать другое расширение C, где они снова вызовут rb_error_frozen напрямую.

Короче говоря: вы можете переопределить функцию C, только если она была явно определена как реализация некоторого метода объекта Ruby, например, как в

rb_define_method(rb_cString, "gsub", rb_str_gsub, -1);

, где вы можетеПредположим, что он будет использоваться только для этой цели.Но даже тогда вы не на 100% безопасны, кто-то может решить повторно использовать эту функцию в какой-то другой части кода C.


Редактировать: Вы сказали, что вы хотите, чтобы Ruby только предупреждал вместо этогоповышения, если замороженный объект был изменен.Я просто просмотрел источники, чтобы узнать, можете ли вы переопределить все места, где вызывается rb_error_frozen.Проблема в rb_check_frozen - он вызывается везде, где объект модифицирован (как и должно быть), и снова сам вызывает rb_error_frozen.Этот механизм глубоко укоренен во внутренних элементах C и не публикуется повсеместно на поверхности Ruby, поэтому нет способа переопределить «поведение поднятия» или, по крайней мере, ни один из них, который не потребовал бы значительных усилий.Если вы думаете об этом на минуту, это на самом деле хорошо.Если бы можно было просто переопределить поведение, то это могло бы рассматриваться как недостаток безопасности в реализации Ruby.Замораживание объекта должно гарантировать, что он останется неизменным, несмотря ни на что.

3 голосов
/ 21 августа 2011

Да, конечно вы можете переопределить методы Ruby, которые реализованы в C (или Java, или C #, или C ++, или Objective-C, или ECMAScript, или как угодно).

Поскольку Ruby являетсяобъектно-ориентированный язык, в значительной степени only , который вы можете сделать в Ruby, это либо переопределить , либо создать новые методы, и, следовательно, в двух наиболее распространенныхиспользуемые реализации Ruby (MRI и YARV) все методы реализованы в C и none в Ruby, вы просто не сможете сделать что-либо , если вы не может переопределить методы, написанные на C.

Однако , есть большая проблема: rb_error_frozen не является методом Ruby.Это функция C.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...