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.]
Question connexe: http://stackoverflow.com/questions/699152/looking-for-a-concrete-example-of -whats-wrong-with-monkey-patching –