Я могу говорить только за МРТ / ЯРВ, но я попробую.Вы можете переопределить функции, которые происходят из 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.Замораживание объекта должно гарантировать, что он останется неизменным, несмотря ни на что.