2010-08-09 7 views
2

Je cherche des exemples de pourquoi ce n'est pas une bonne idée d'étendre les classes de base dans ruby. J'ai besoin de montrer à certaines personnes pourquoi c'est une arme à manier avec précaution.Pièges Ruby singe patching

Des histoires d'horreur que vous pouvez partager?

+0

Question connexe: http://stackoverflow.com/questions/699152/looking-for-a-concrete-example-of -whats-wrong-with-monkey-patching –

Répondre

5

Il y avait un exemple assez célèbre de monkey-patching going horribly wrong il y a environ 2,5 ans dans Rubinius. La chose intéressante à propos de ce cas est que le code fautif et la victime étaient très visibles et très inhabituels. Habituellement, le délinquant est un morceau de code écrit par un script PHP kiddy qui s'est saoulé sur son skillz 1339 metaprogramming h4X0r. Et le mode de défaillance est une exception simple, car la méthode d'origine et le monkeypatch ont une arité différente.

Cependant, dans ce cas, le délinquant était une bibliothèque dans le fichier stdlib (mathn) et le mode de défaillance était le Rubinius VM explosant complètement.

Alors, que s'est-il passé? Eh bien, mathn monkeypatches la classe Fixnum et change comment fonctionne arithmétique Fixnum. En particulier, il modifie à la fois les résultats et les types de plusieurs méthodes de base. Par exemple:

r = 4/3 # => 1 
r.class # => Fixnum 

require 'mathn' 

r = 4/3 # => (4/3) 
r.class # => Rational 

Le problème est bien sûr que Rubinius, l'ensemble du compilateur Ruby, l'ensemble du noyau Ruby, une grande partie de la bibliothèque de base Ruby, certaines parties de la machine virtuelle Rubinius et d'autres parties de l'infrastructure Rubinius, sont tous écrits en Ruby. Et bien sûr, tous ceux utilisent l'arithmétique Fixnum partout.

La classe Hash est écrit en Ruby et utilise Fixnum arithmétique pour calculer la taille des seaux de hachage, calculer la fonction de hachage et ainsi de suite. Array est écrit en Ruby et doit calculer les tailles d'éléments et les longueurs de tableau. La bibliothèque FFI est écrite en Ruby et doit calculer les adresses de mémoire (!) Et les tailles de structure. De nombreuses parties de Rubinius supposent qu'elles peuvent effectuer une opération arithmétique et ensuite passer le résultat à une fonction C comme un pointeur ou int. Et puisque Ruby ne supporte pas n'importe quel type de sélecteur de noms ou boxe de classe ou similaire (bien que quelque chose comme ça soit prévu pour Ruby 2.0), dès qu'un code utilisateur aléatoire nécessite la bibliothèque mathn, toutes ces pièces exploser spectaculairement, parce que tout à coup, le résultat d'une opération Fixnum n'est plus un Fixnum (qui est fondamentalement identique à une machine int et peut être transmis comme tel), mais un Rational (qui est un objet Ruby à part entière)).

Fondamentalement, ce qui arriverait, c'est qu'un certain code serait require 'mathn' (ou vous le taperiez dans IRb), et immédiatement la VM mourrait juste.

La solution, dans ce cas, était le plugin maths sûr pour le compilateur: lorsque le compilateur détecte qu'il est compilait le noyau ou d'autres parties essentielles de Rubinius, il réécrit automatiquement les appels vers Fixnum méthodes dans les appels vers privé copies immuables de ces méthodes. [Note: Je pense que dans les versions actuelles de Rubinius, le problème est résolu d'une manière différente.]

+0

Ouais c'est vraiment effrayant. Et cela ressemble à un cauchemar complet à traquer. – zaius

+0

Ce bug m'a juste mordu aujourd'hui. Y compris le "twss" plugin pour le chat de ma société inclus classificateur, qui comprenait mathn, qui a brisé le linkbot en brisant la division entière. Soupir. – llimllib

1

Un collimateur évident serait les collisions de noms - si deux ou plusieurs paquets choisissent le même nom pour une méthode qui se comporte différemment.

Questions connexes