Belirli bir kod parçasının işlevselliği ile ilgili olarak başka bir programcı veya denetçi tarafından hemen açık olmayan kod bölümlerinde saklanabilecek dinamik dillerde yapılabilecek birkaç 'temiz' şey vardır.
İrb'deki bu diziyi göz önünde bulundurun (etkileşimli yakut kabuğu):
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!"
Orada olanlar foo
bir String sabitinde metodu çağırmaya çalıştım . Bu başarısız oldu. Daha sonra String sınıfını açtım ve foo
return metodunu tanımladım "foobar!"
ve sonra çağırdım. Bu çalıştı.
Bu açık bir sınıf olarak bilinir ve her zaman bir güvenlik veya bütünlüğe sahip olan yakutta kod yazmayı düşündüğümde bana kabuslar verir. Tabii ki çok hızlı bir şekilde bazı şeyleri hızlıca yapabilmenizi sağlıyor ... ama bir dize sakladığında, bir dosyada sakladığında veya ağ üzerinden gönderdiğinde bunu başarabilirim. Ve String'i yeniden tanımlamanın bu küçük kısmı kodun herhangi bir yerinde saklanabilir.
Diğer birçok dinamik dilde yapılabilecek benzer şeyler vardır. Perl'de, sahnelerin ardında, belirli bir skalerin çalışma şeklini değiştirebilecek Tie :: Skaler var (bu biraz daha açıktır ve görebileceğiniz belirli bir komut gerektirir, ancak başka bir yerden geçen bir skaler sorun olabilir). Perl Yemek Kitabına erişiminiz varsa, bkz. Tarif 13.15 - Kravatla Büyü Değişkenleri Yaratma.
Bu şeyler nedeniyle (ve diğerleri genellikle dinamik dillerin bir parçası), kodda güvenliğin statik analizine pek çok yaklaşım işe yaramıyor. Perl ve Belirlenemezlik bunun durum olduğunu gösterir ve sözdizimi vurgulama gibi önemsiz sorunları bile işaret eder ( whatever / 25 ; # / ; die "this dies!";
zorlukları ortaya çıkarır çünkü whatever
argümanlar almak için tanımlanabilir veya çalışma zamanında bir sözdizimi vurgulayıcıyı veya statik analizörü tamamen yenmeden tanımlanamaz ).
Bu, kapatmanın tanımlandığı ortama erişme yeteneği ile Ruby'de daha da ilginçleşebilir ( YouTube: RubyConf 2011'den Joshua Ballanco tarafından Ruby'nin Makul Olmasını Sağlama). MouseTheLuckyDog tarafından yayınlanan bir Ars Technica yorumundan bu videodan haberdar oldum .
Aşağıdaki kodu göz önünde bulundurun:
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
Bu kod tamamen görülebilir, ancak mal
yöntem başka bir yerde olabilir ... ve açık sınıflarla, elbette, başka bir yerde yeniden tanımlanabilir.
Bu kodu çalıştırıyorum:
~ / $ ruby foo.rb
bar
> :)
qux
bar
b.rb: 20: `qux 'cinsinden: MWHWAHAW! (Çalışma hatası)
b.rb'den: 30: `'
~ / $ ruby foo.rb
bar
> :)
qux
b.rb: 20: `bar ': MWHWAHAW! (Çalışma hatası)
b.rb'den: 29: `'
Bu kodda, kapatma, sınıfta tanımlanan tüm yöntemlere ve diğer bağlamalara bu kapsamda erişebildi . Rasgele bir yöntem seçti ve bir istisna oluşturmak için yeniden tanımladı. ( Bu nesnenin neye erişimi olduğu hakkında bir fikir edinmek için Ruby'deki Binding sınıfına bakın )
Değişkenler, yöntemler, benlik değeri ve muhtemelen bu bağlamda erişilebilen bir yineleyici bloğu korunur.
Bir değişkenin yeniden tanımlanmasını gösteren daha kısa bir sürüm:
def mal(&block)
block.call
block.binding.eval('a = 43')
end
a = 42
puts a
mal do
puts 1
end
puts a
Hangi, run ürettiğinde:
42
1
43
Bu, statik analizi imkansız kılan yukarıda bahsettiğim açık sınıftan daha fazlası. Yukarıda gösterilen şey, başka bir yere iletilen bir kapağın, içinde tanımlandığı tüm ortamı taşıdığıdır. Bu, birinci sınıf bir ortam olarak bilinir (tıpkı fonksiyonların etrafında dolaşırken, bunlar birinci sınıf fonksiyonlardır; bu çevre ve o andaki mevcut bütün bağlanmalardır). Bir kapatma kapsamında tanımlanan herhangi bir değişkeni yeniden tanımlayabilirsiniz .
Yakut veya olmasın şikayet, iyi ya da kötü (tek olur kullanımları vardır istiyorum bir yöntemin ortamda elde edebilmek için (bkz Güvenli ) Perl), neden yakut olurdu" sorusu bir hükümet projesi için sınırlama getirilmesini "gerçekten bağlantılı bir videoda yanıtlandı.
Verilen:
- Ruby, insanın çevreyi herhangi bir kapanmadan açmasını sağlar
- Ruby, kapatma kapsamındaki tüm ciltleri yakalar
- Ruby, tüm ciltleri canlı ve değişken olarak korur
- Ruby'nin yeni bağları eski bağları gölgeledi (çevreyi klonlamak ya da yeniden bağlamayı yasaklamak yerine)
Bu dört tasarım seçeneğinin getirdiği sonuçlarla, herhangi bir kod parçasının ne yaptığını bilmek mümkün değildir.
Bu konuda daha fazla Özet Heresies blogunda okunabilir . Özel görev, böyle bir tartışmanın yapıldığı Şema ile ilgilidir. (SO ile ilgili: Scheme neden birinci sınıf ortamları desteklemiyor? )
Ancak zamanla, birinci sınıf ortamlarda ilk başta düşündüğümden daha fazla güç ve daha az güç olduğunu fark ettim. Bu noktada birinci sınıf ortamların en iyi ihtimalle işe yaramaz, en kötüsü de tehlikeli olduğuna inanıyorum.
Umarım bu bölüm birinci sınıf ortamların tehlike yönünü ve neden Ruby'nin sağlanan çözümden kaldırılması isteneceğini göstermektedir. Bu sadece Ruby'nin dinamik bir dil olması değil (başka bir cevapta belirtildiği gibi, diğer projelerde diğer dinamik dillere de izin verilmiştir) değil, aynı zamanda bazı dinamik dillerin nedenini daha da zorlaştıracak belirli konular olduğunu göstermektedir.