Es gibt eine Reihe von 'netten' Dingen, die in dynamischen Sprachen erledigt werden können und die in Teilen des Codes versteckt werden können, die für einen anderen Programmierer oder Prüfer hinsichtlich der Funktionalität eines bestimmten Codeteils nicht sofort ersichtlich sind.
Betrachten Sie diese Sequenz in irb (interaktive Rubinhülle):
irb(main):001:0> "bar".foo
NoMethodError: undefined method `foo' for "bar":String
from (irb):1
from /usr/bin/irb:12:in `<main>'
irb(main):002:0> class String
irb(main):003:1> def foo
irb(main):004:2> "foobar!"
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> "bar".foo
=> "foobar!"
Was dort passiert ist, ist, dass ich versucht habe, die Methode foo
in einer String-Konstante aufzurufen . Dies ist fehlgeschlagen. Ich habe dann die String-Klasse geöffnet und die Methode foo
o return definiert "foobar!"
und sie dann aufgerufen. Das hat funktioniert.
Dies ist als offene Klasse bekannt und bringt mich jedes Mal in Albträume, wenn ich daran denke, Code in Ruby zu schreiben, der jede Art von Sicherheit oder Integrität aufweist. Sicher, damit können Sie einige nette Dinge sehr schnell erledigen ... aber ich könnte es so machen, dass jedes Mal, wenn jemand eine Zeichenfolge speichert, diese in einer Datei gespeichert oder über das Netzwerk gesendet wird. Und dieses kleine Stück der Neudefinition des Strings kann an einer beliebigen Stelle im Code verstaut werden.
Viele andere dynamische Sprachen haben ähnliche Möglichkeiten. Perl hat Tie :: Scalar , mit dem sich die Funktionsweise eines bestimmten Skalars im Hintergrund ändern lässt (dies ist etwas offensichtlicher und erfordert einen bestimmten Befehl, den Sie sehen können, aber ein Skalar, der von einem anderen Ort übergeben wird, könnte ein Problem darstellen). Wenn Sie Zugriff auf das Perl-Kochbuch haben, schlagen Sie Rezept 13.15 - Erstellen magischer Variablen mit Krawatte nach.
Aufgrund dieser Faktoren (und anderer Faktoren, die häufig Teil dynamischer Sprachen sind) funktionieren viele Ansätze zur statischen Analyse der Sicherheit im Code nicht. Perl und Undecidability zeigen, dass dies der Fall ist, und weisen auch auf solche trivialen Probleme bei der Syntaxhervorhebung hin (dies ist whatever / 25 ; # / ; die "this dies!";
eine Herausforderung, da whatever
definiert werden kann, ob Argumente zur Laufzeit verwendet werden sollen oder nicht, wenn ein Syntax-Textmarker oder ein statischer Analysator vollständig beseitigt wird).
Dies kann in Ruby mit der Möglichkeit, auf die Umgebung zuzugreifen, in der ein Abschluss definiert wurde, noch interessanter werden (siehe YouTube: Halten von Ruby von RubyConf 2011 von Joshua Ballanco vernünftig ). Auf dieses Video wurde ich durch einen Kommentar von MouseTheLuckyDog von Ars Technica aufmerksam gemacht .
Betrachten Sie den folgenden Code:
def mal(&block)
puts ">:)"
block.call
t = block.binding.eval('(self.methods - Object.methods).sample')
block.binding.eval <<-END
def #{t.to_s}
raise 'MWHWAHAW!'
end
END
end
class Foo
def bar
puts "bar"
end
def qux
mal do
puts "qux"
end
end
end
f = Foo.new
f.bar
f.qux
f.bar
f.qux
Dieser Code ist vollständig sichtbar, aber die mal
Methode könnte woanders sein ... und bei offenen Klassen könnte sie natürlich woanders neu definiert werden.
Diesen Code ausführen:
~ / $ ruby foo.rb
Bar
> :)
qux
Bar
b.rb: 20: in `qux ': MWHWAHAW! (Laufzeit Fehler)
von b.rb: 30: in `'
~ / $ ruby foo.rb
Bar
> :)
qux
b.rb: 20: in `bar ': MWHWAHAW! (Laufzeit Fehler)
von b.rb: 29: in `'
In diesem Code konnte der Abschluss auf alle Methoden und andere Bindungen zugreifen, die in der Klasse in diesem Bereich definiert wurden. Es wurde eine zufällige Methode ausgewählt und neu definiert, um eine Ausnahme auszulösen. (In der Binding- Klasse in Ruby erfahren Sie, worauf dieses Objekt zugreifen kann.)
Die Variablen, Methoden, der Wert von self und möglicherweise ein Iteratorblock, auf den in diesem Kontext zugegriffen werden kann, bleiben erhalten.
Eine kürzere Version, die die Neudefinition einer Variablen zeigt:
def mal(&block)
block.call
block.binding.eval('a = 43')
end
a = 42
puts a
mal do
puts 1
end
puts a
Was, wenn ausgeführt wird, ergibt:
42
1
43
Dies ist mehr als die oben erwähnte offene Klasse, die eine statische Analyse unmöglich macht. Oben wird gezeigt, dass ein Abschluss, der an eine andere Stelle übergeben wird, die vollständige Umgebung enthält, in der er definiert wurde. Dies wird als erstklassige Umgebung bezeichnet (genau wie beim Umgeben von Funktionen handelt es sich um erstklassige Funktionen. Dies ist die Umgebung und alle zu diesem Zeitpunkt verfügbaren Bindungen. Man kann jede Variable neu definieren, die im Rahmen des Abschlusses definiert wurde.
Gut oder schlecht, sich über Ruby zu beschweren oder nicht (es gibt Anwendungen, bei denen man in die Umgebung einer Methode gelangen möchte (siehe Safe in Perl)), die Frage "Warum sollte Ruby für ein Regierungsprojekt eingeschränkt werden?" "wird in dem oben verlinkten Video wirklich beantwortet.
Vorausgesetzt, dass:
- Ruby erlaubt es einem, die Umgebung von jedem Verschluss zu extrahieren
- Ruby erfasst alle Bindungen im Rahmen des Verschlusses
- Ruby behält alle Bindungen als lebendig und wandelbar bei
- Ruby hat neue Bindungen als Schatten alter Bindungen (anstatt die Umgebung zu klonen oder ein erneutes Binden zu verbieten)
Mit den Auswirkungen dieser vier Entwurfsoptionen ist es unmöglich zu wissen, was ein Codebit bewirkt.
Mehr dazu können Sie im Abstract Heresies Blog nachlesen . In dem speziellen Beitrag geht es um das Schema, in dem eine solche Debatte geführt wurde. (bezogen auf SO: Warum unterstützt Scheme keine erstklassigen Umgebungen? )
Mit der Zeit wurde mir jedoch klar, dass erstklassige Umgebungen schwieriger und leistungsärmer waren, als ich ursprünglich gedacht hatte. An diesem Punkt glaube ich, dass erstklassige Umgebungen bestenfalls nutzlos und im schlimmsten Fall gefährlich sind.
Ich hoffe, dieser Abschnitt zeigt den Gefahrenaspekt von erstklassigen Umgebungen und warum Ruby aus der bereitgestellten Lösung entfernt werden sollte. Ruby ist nicht nur eine dynamische Sprache (wie bereits erwähnt, andere dynamische Sprachen wurden in anderen Projekten zugelassen), sondern es gibt auch spezielle Probleme, die es noch schwieriger machen, über einige dynamische Sprachen nachzudenken.