SwiftProgramlamaSwift Geliştirici

Hangi derleme aşamasında **Swift** ekli makroları genişletir ve hangi hijyenik mekanizma предотвращает конфликт имен в сгенерированном коде?

Hintsage yapay zeka asistanı ile mülakatları geçin

Sorunun cevabı.

Swift makroları, derleme sürecinin anlamsal analiz aşamasında, özellikle son Soyut Sözdizim Ağaçlarının (AST) tür kontrolünden önce, ayrıştırmadan sonra genişletilir. Bu zamanlama önemlidir çünkü makro genişlemesi, tam tür kontrolüne ve anlamsal doğrulamaya tabi tutulması gereken kodu üretmesine olanak tanır. Bu aşamada çalışan Swift, genişletilmiş kodun dilin tür güvenliği garantilerini ihlal edememesini veya erişim kontrolü değişkenlerini atlayamamasını sağlar.

Sorun, makroların kaynak kodu, yeni sözdizim düğümleri üreterek dönüştürmesinden kaynaklanmaktadır; bu da çevredeki leksik kapsamda mevcut değişkenlerle çelişebilecek tanımlayıcıları potansiyel olarak tanıtabilir. Bir makro, basitçe kodlanmış değişken adlarını enjekte ediyorsa, çağıran bağlamdan değişkenleri kazara yakalayabilir veya gölgede bırakabilir. Bu, oluşturulan kodun çağıranın mantığıyla etkileşime girdiği durumlarda ince hataların veya güvenlik açıklarının ortaya çıkmasına yol açabilir.

Bunu çözmek için, Swift tüm sentetik bağlamalar için benzersiz iç tanımlayıcılar kullanan hijyenik bir makro sistemi uygular. Derleyici, sözdizim düğümlerine, orijinal leksik bağlamlarını izleyen meta veriler ekler ve böylece oluşturulan tanımlayıcıların kullanıcı yazılı kodu ile karıştırılmadan ayrı muamele görmesini sağlar. Bu mekanizma, makroların geçici değişkenleri güvenli bir şekilde tanıtmasına izin verirken, istenildiğinde açık parametre geçişi yoluyla kasıtlı ad yakalamalarına da izin verir.

Hayattan bir durum

Ekibimiz, karmaşık hizmet sınıfları için otomatik olarak başlatıcı kodu oluşturmak amacıyla @Injectable adlı bir ekli makro kullanan bir Swift paketi geliştiriyordu. Makro, oluşturma sırasında ara bağımlılıkları tutmak için geçici değişkenler oluşturmak zorundaydı, ancak hedef sınıf kapsayıcısında container veya service gibi yaygın değişken adlarının zaten mevcut olabileceği riskine maruz kaldık. Bu, bize bir ikilem yarattı: İsim çakışmalarını riske atmadan, güvenli bir başlatma kodu nasıl üretebilirdik ki bu, istemci kodunu bozmasın veya ince yeniden atama hatalarını önlesin?

Öncelikle, basit metin tabanlı bir kod üretim yaklaşımını uygulamayı düşündük; basit dize şablonları kullanarak başlatıcı uygulamasını üretmek. Temel avantaj, üretilen Swift kodunu hemen inceleyip doğrudan hata ayıklamaya başlama basitliğiydi. Ancak, hayati dezavantaj, hijyen garantilerinin olmamasıydı; geçici değişken adlarının mevcut özelliklerle çelişmeyeceğini sağlamak için yeterli bir mekanizma yoktu, bu da potansiyel derleme hataları veya makronun mevcut örnek değişkenlerini kazara yeniden atadığı durumlarda sessiz mantık hatalarına yol açabilirdi.

Ardından, Sourcery adlı olgun bir üçüncü şahıs kod üretim aracını değerlendirdik; bu araç, Swift derleyicisi dışındaki bir ön-derleme adımı olarak çalışır. Avantajları arasında kapsamlı belgeler, esnek şablon şablonları ve yalnızca satır içi kod değil, tüm dosyaları üretme yeteneği bulunmaktaydı. Ne yazık ki, dezavantajları arasında karmaşık derleme aracı entegrasyonu, Xcode'da ek Run Script aşamaları gerektirmesi, dış süreç yükü nedeniyle daha yavaş derleme süreleri ve üretilen kodda tür hatalarının yalnızca derleme zamanında yüzeye çıkmasına neden olan gerçek zamanlı anlamsal analiz eksikliği bulunmaktaydı; bu, hata yerlerini, orijinal makro çağrısına net bir kaynak haritalaması ile bağlamadı.

Sonunda, Swift 5.9'da tanıtılan Swift'in yerel makro sistemini, hizmet sınıfı bildirimine ekli bir akraba makro kullanarak seçtik. Bu çözüm, derleme zamanında genişletilmiş kodun tür kontrolünü sağlamak ve oluşturulan tanımlayıcılar için hijyen sağlamak amacıyla derleyici hattına doğrudan entegre olduğu için seçildi; bu, SwiftSyntax kütüphanesi aracılığıyla gerçekleşti. Sonuç olarak, @Injectable makrosunun, ad gölgelenmesi riski olmadan karmaşık başlatma mantığını güvenli bir şekilde üretebildiği sağlam bir bağımlılık enjeksiyon çerçevesi ortaya çıktı, bürokratik kodu yaklaşık %70 oranında azaltırken tam derleme zamanı güvenliği garantileri ve doğrudan makro kullanım yerine işaret eden net hata mesajları sağladı.

Son uygulama, önceki manuel bağımlılık enjeksiyon düzenimizi tehdit eden isimle ilişkili hataların tüm bir kategorisini ortadan kaldırdı. Derleme süreleri, Sourcery yaklaşımına kıyasla %40 oranında iyileşti ve geliştiriciler, makro tarafından üretilen başlatıcıların yeni bağımlılıklara otomatik olarak uyum sağlayacağından emin olarak hizmet sınıflarını rahatlıkla yeniden yapılandırabildiler.

Adayların sıkça göz ardı ettiği şeyler


Neden Swift'teki makrolar mevcut kodu yerinde değiştiremez ve benzer anlamları elde eden alternatif desenler nelerdir?

Lisp veya Rust'taki prosedürel makroların mevcut sözdizim düğümlerini yerinde dönüştürebileceği gibi, Swift makroları tamamen ekleyicidir - yalnızca yeni kod üretebilir, asıl kaynakları asla değiştiremez. Bu kısıtlama, Swift'in derleme modelinin, hata ayıklama, kaynak eşleme ve artımlı derleme amaçları için orijinal kaynağın bütünlüğünü korumasını gerektirdiği için vardır. "Değiştirme" anlamlarını elde etmek için, geliştiricilerin ek yüklemeler veya sarıcı türler üreten akraba makrolar kullanmaları ve göçü yönlendirmek için orijinal bildirimlerdeki iptal anotasyonları ile birlikte kullanmaları gerekir.


Makro genişlemesi, üretilen ifadeler için tür çıkarımını nasıl işliyor ve çıkarım başarısız olursa ne oluyor?

Bir makro, açık tür açıklamaları içermeyen ifadelerle kod oluşturduğunda, Swift, makro genişlemesinden sonra meydana gelen standart tür kontrolü aşamasında üretilen AST üzerinde tür çıkarımı gerçekleştirir. Eğer çıkarım başarısız olursa, derleyici, hata yerlerini genişleme sırasında eklenen kaynak konum meta verileri kullanarak makro çağrısı yerine geri eşleştiren tanı mesajları yayımlar. Adaylar, makroların açıkça #file ve #line literaalleri üretebileceğini veya tanılardaki kullanıcıya nasıl göründüğünü kontrol etmek için #sourceLocation yönergesini kullanabileceğini sıklıkla göz ardı eder; bu, hataların anlamlı konumlara yönlendirilmesini sağlar ve iç makro uygulama ayrıntılarına işaret etmez.


Serbest ve ekli makrolar, genişletme bağlamları ve mevcut anlamsal bilgiler açısından ne fark vardır?

Serbest makrolar ( # ile ön ekli) ifade veya bildirim seviyesinde genişler ve çevredeki tür bilgisine sınırlı erişime sahiptir; yalnızca argümanlarının sözdizimini alırlar. Aksine, ekli makrolar ( @ ile ön ekli) bildirimler üzerinde çalışır ve ekli bildirimlerin sözdizimi, erişim değişkenleri ve miras ilişkileri dahil zengin anlamsal bilgiler alırlar; bu bilgiler macro bildiriminin bağlam parametresi üzerinden sağlanır. Başlangıç seviyesindeki kullanıcılar, tür üyelerine erişmek veya belirli tür kapsamlarındaki iç içe bildirimler oluşturmak için gereken akraba veya üye makrolar yerine serbest makroları kullanmaya çalışırken bu sınırları sıkça karıştırırlar.