Kann ich Ruby-Methoden in C überschreiben?

8

Ist es möglich, Methoden, die Teil von Ruby selbst sind, wie rb_error_frozen , die in C geschrieben sind, mit Ruby-Code zu umgehen?

Hintergrund : Ich frage mich, ob es möglich ist, dass Ruby lediglich eine Warnung protokolliert, anstatt eine Ausnahme auszulösen, wenn ein eingefrorenes Objekt geändert wird. Auf diese Weise kann ich eine Vielzahl von Zustandsänderungen protokollieren, anstatt zu stoppen, wenn die erste auftritt.

Ich denke in erster Linie daran, dies mit YARV zu tun, aber ich könnte eine andere Implementierung verwenden, wenn es das einfacher macht.

Und ja, es ist ein tolles Projekt! Versuchen Sie das nicht in einer Produktionsumgebung!

    
Andrew Grimm 21.08.2011, 13:51
quelle

2 Antworten

5

Ich kann nur für MRI / YARV sprechen, aber ich werde es versuchen. Sie können nur Funktionen von C in Ruby überschreiben, wenn die C-Funktion explizit als Methode für ein Ruby-Objekt definiert wurde. Zum Beispiel ist Kernel#extend explizit in C als

definiert %Vor%

Also, weil die C-Funktion rb_obj_extend (in Anführungszeichen, weil ich bildlich gesprochen habe, ich meine nicht C-Verknüpfung) mit der Methode Kernel#extend in der Ruby-Welt "verlinkt" war, theoretisch könnten Sie das tun Überschreiben Sie das Verhalten von rb_obj_extend , wenn Sie Kernel#extend überschreiben.

Ich würde sagen, dass man unter den folgenden zwei Bedingungen behaupten könnte, dass Sie tatsächlich eine rb_ * C-Funktion "übersteuert":

  • Die rb_ * C-Funktion wurde mit einem Ruby-Objekt "verknüpft", so dass wir in Ruby eine Handle haben, die als Hook dient, den wir überschreiben können
  • Die angegebene rb_ * -Methode wird nur für genau diesen Zweck in diesem einen Punkt verwendet, sie wird nirgendwo anders wiederverwendet

Wenn Sie nun rb_error_frozen betrachten, erfüllt es keine dieser beiden Bedingungen. Es ist ein Helfer in der C-Implementierung, dh es wird von mehreren Stellen aufgerufen. Und es wurde nicht explizit mit einem Ruby-Objekt "verknüpft", so dass Sie keinen Haken haben, wo Sie es überschreiben könnten.

Es ist jedoch nicht alles verloren. Sie können rb_error_frozen nicht direkt überschreiben, aber Sie könnten trotzdem versuchen, alle Ruby-Methoden zu überschreiben, bei denen rb_error_frozen auf die "Ruby-Oberfläche" aufsteigt. Damit meine ich, dass Sie alle Stellen in den C-Quellen überprüfen können, in denen rb_error_frozen verwendet wird, und von diesen Stellen aus versuchen, jede Ruby-Methode zu finden, die diese Code-Bits auslösen könnte. Wenn dies eine geschlossene Menge ist, können Sie einfach alle diese Methoden überschreiben, um das Verhalten von rb_error_frozen "de-facto-override" zu setzen.

Dies ist jedoch nur eine Patchwork-Lösung. All Ihre harte Arbeit geht verloren, sollte sich jemand entscheiden, eine andere C-Erweiterung zu schreiben, wo sie% code_% direkt wieder aufrufen.

Kurz gesagt: Sie können eine C-Funktion nur überschreiben, wenn sie explizit als Implementierung einer Methode eines Ruby-Objekts definiert wurde, z. wie in

%Vor%

wo Sie davon ausgehen können, dass es immer nur für diesen Zweck verwendet wird. Aber auch dann, wenn Sie nicht hundertprozentig sicher sind, könnte sich jemand dafür entscheiden, diese Funktion in einem anderen Teil des C-Codes wiederzuverwenden.

Bearbeiten: Sie sagten, Sie möchten, dass Ruby nur warnt, anstatt zu erhöhen, wenn ein eingefrorenes Objekt geändert wurde. Ich habe gerade die Quellen durchsucht, um zu sehen, ob Sie alle Orte überschreiben können, an denen rb_error_frozen aufgerufen wird. Das Problem ist rb_error_frozen - es wird überall aufgerufen, wo ein Objekt geändert wird (wie es sein sollte) und ruft wiederum selbst rb_check_frozen auf. Dieser Mechanismus ist tief in den C-Interna verwurzelt und nicht überall auf der Ruby-Oberfläche veröffentlicht, so dass es keine Möglichkeit gibt, das "Raising-Verhalten" außer Kraft zu setzen, oder zumindest keine, die keinen nennenswerten Aufwand erfordern würde. Wenn du eine Minute darüber nachdenkst, ist das eigentlich eine gute Sache. Wenn es möglich wäre, das Verhalten einfach zu überschreiben, könnte dies tatsächlich als eine Sicherheitslücke in der Ruby-Implementierung angesehen werden. Einfrieren eines Objekts sollte Ihnen garantieren, dass es nicht verändert werden kann, egal was passiert.

    
emboss 21.08.2011, 14:37
quelle
3

Ja, natürlich Sie können Ruby-Methoden, die in C (oder Java oder C # oder C ++ oder Objective-C oder ECMAScript oder was auch immer) implementiert sind, außer Kraft setzen.

Da es sich bei Ruby um eine objektorientierte Sprache handelt, können Sie nur in überschreiben oder neu erstellen Methoden, und da auf den beiden am häufigsten verwendeten Ruby-Implementierungen (MRI und YARV) alle Methoden in C und keine in Ruby implementiert werden, wäre es einfach nicht möglich alles tun, wenn Sie nicht Methoden überschreiben können, die in C geschrieben wurden.

Allerdings gibt es ein großes Problem: rb_error_frozen ist keine Ruby-Methode. Es ist eine C-Funktion.

    
Jörg W Mittag 21.08.2011 14:42
quelle

Tags und Links