Teknik Makale

Pascal PDF Kitaplığında Sessiz Stub Hatalarını Teşhis Etmek

Bir Delphi kitaplığı görsel çerçeve olmadan bir derleme yapılandırması kazandığında, hataların yaşadığı yer ikame sınıflardır. Ne platform ne derleyici: yerine geçenler. PDFlibPas, VCL olmadan derlemeler için bitmap, canvas, font, metafile ve yazıcı eşdeğerleri sağlayan bir grafik katmanına sahiptir ve onu Free Pascal'a taşımak, bir ikamenin sahip olabileceği her hata modunu yüzeye çıkardı. Bunlar teşhis maliyetine göre derli toplu sıralanır ve sıralama, sezginin ima ettiğinin tam tersidir

Atan bir ikameyi bulmak ucuzdur; istisna yöntemin adını verir. Boş veri döndüren bir ikame pahalıdır, çünkü hata nedeninden birkaç katman ötede görünür. Başarı döndüren ikame hepsinden kötüdür, çünkü dönüş kodu geçerlidir, hata kodu sıfırdır, hiçbir istisna atılmaz ve ters giden tek bir şey olduğuna dair tek kanıt çıkan baytların içindedir

Pascal PDF kitaplığında sessiz stub hatasının üç biçimi, teşhis maliyetine göre sıralanmış; atan stubdan boş çıktı üzerinde başarıya
Atan ikameyi teşhis etmek ucuzdur, boş veri pahalıdır ve boş bir artefakt üzerinde başarı döndürmek en kötü bulunan olandır

Üçüncü biçim: boş bir XObject üzerinde geçerli bir görüntü tanıtıcısı

Vektör metafile dönüştürücüsü, non-VCL yapılandırmada boş bir yordam gövdesiydi. Üstündeki her şey çalışmaya devam etti. EMF içe aktarma giriş noktaları ve canvas yakalama giriş noktası sonuna dek çalıştı ve yasal bir görüntü tanıtıcısı döndürdü; çağıran da onu bir sayfaya yerleştirdi. Dosyaya düşen şey, içerik uzunluğu sıfır olan bir form XObject oldu. Sayfa beyaz çizildi

Hiçbir şey sorun bildirmedi; buna kitaplığın bu özellik için kendi örnek programı da dahildir, boş bir sayfa çizdi ve fark etmedi. Denetlenecek başarısız bir dönüş değeri yoktu, çünkü çağrı dizisi gerçekten de tamamen başarılıydı; yanlış olan tek şey üretilen akışın boyutuydu. Bu hata sınıfını teşhis etmek başka bir soru sormak demektir: "çağrı başarısız mı oldu" değil, "artefakt inandırıcı mı". Uzunluğu sıfır olan bir form XObject, pikseli sıfır olan bir görüntü, içerik baytı sıfır olan bir sayfa; onu yakalayan savlar bunlardır

Düzeltmenin iki yarısı var ve ikinci yarısı unutması kolay olandır. Birincisi, boş uygulamayı atar hâle getirmek, böylece hatanın hiç değilse bir kanalı olsun. İkincisi, o istisnayı görüntü fabrikasında null bir sonuca çevirmek ve görüntü tanıtıcısı tüketen iki yere null denetimleri eklemektir; yoksa "temiz hata", sayfa ağacı hiçbir şeyin referansını çözerken doğrudan bir erişim ihlaline döner. Atan bir stub, yalnızca çağıranlar daha önce hiç alamadıkları bir hataya hazırlıklıysa bir iyileştirmedir

İkinci biçim: boş veri, çökmeden üç katman ötede

Metafile canvas ikamesi fiziksel boyutlarını doldurmadı. O değer bir sayfa geometrisi hesaplamasına bölünür, hesaplama sıfır üretti ve sınırlayıcı kutu hesabı sıfıra bölündü. Çıplak bir istisna işleyici bunu yuttu, görüntü fabrikası null bir sonuç döndürdü ve erişim ihlali nihayet null kullanıldığında sayfa ağacında gerçekleşti. Neden ile belirti arasında üç katman var ve ortadaki istisna işleyici kanıtı siliyor

PDFlibPas'ta boş metafile canvas ikamesinin hata zinciri; sıfıra bölmeden üç katman sonra bir erişim ihlaline ulaşır
Boş boyut bir geometri hesaplamasını sıfıra böler, çıplak işleyici istisnayı siler ve null tanıtıcı sayfa ağacını çökertir

Aynı birimde bu desenin iki örneği daha vardı. Font sınıfının boş Assign ve kurucu gövdeleri vardı; göründüğünden daha önemli, çünkü canvas font özelliği salt okunurdur: içine atama yapmak bir fontu ulaştırmanın tek yoludur, böylece boş bir uygulama font seçimini sessizce etkisiz kılar ve metin varsayılan neyse onunla çıkar. Ve inç başına piksel değerinin sıfır olması, font metriklerinden bir canvas boyutlayan her çağıranın sıfıra sıfır bir canvas üretmesine yol açtı; sonuç boş bir sayfa ve başarılı bir dönüş

// Bir ikame biriminde aranacak biçim: ne atan ne de bir şey yapan
// bir yöntem. İkisi de derlenir ve ikisi de çıktısız "başarı"
// üretir
procedure TMetafileCanvasStandIn.Create(...);
begin
  // inherited çağrısı yok, alan başlatması yok
end;

function TBitmapStandIn.LoadFromStream(Stream: TStream): Boolean;
begin
  Result := True;    // ve bitmap hâlâ boş
end;

Yalnızca ilk karakteri tutan geniş yapı

Bu hiçbir stand-in sorunu değildir ama aynı kataloğa aittir, çünkü belirti de nedenden aynı uzaklıktadır. Yazıcı numaralandırma yapısının on iki dize üyesinin tamamı tek baytlık karakter işaretçileri olarak bildirilmişti; oysa onu dolduran işlev, numaralandırma API'sinin geniş karakter varyantıdır

İşaretçi boyutları özdeştir, dolayısıyla yapı düzeni doğrudur ve hiçbir şey çökmez. Bunun yerine olan şey, bir UTF-16 dizesinin tek baytlık dize olarak okunmasının ilk sıfır baytta durmasıdır; herhangi bir ASCII yazıcı adı için bu, ikinci karakterin yüksek yarısıdır. Her yazıcı adı tam olarak tek karakter olarak geri geldi. Aşağı akışta ad doğrulaması başarısız oldu, yazıcı oluşturma başarısız oldu ve makinedeki her gerçek yazıcı için yazdırma başarısız oldu; ve o belirtilerin hiçbiri bir yapı bildirimine işaret etmiyor

Geniş bir Win32 yapısının PWideChar yerine PAnsiChar üyelerle bildirilmesinden sonra UTF-16 yazıcı adının tek karaktere kısaltılması
Tek baytlık üyeler bir UTF-16 adını yalnızca ilk sıfır baytına dek okur, dolayısıyla her yazıcı adı tam olarak tek karakterle geri döner
// Yanlış: doğru boyut, yanlış eleman türü. Derleme hatası yok,
// çökme yok, her dize tek karaktere kısaltılmış
type
  TPrinterInfo2Wrong = record
    pServerName: PAnsiChar;
    pPrinterName: PAnsiChar;
    // ... on tane daha
  end;

// Doğru: bir *W yapısı baştan sona geniş üyeler taşır
type
  TPrinterInfo2W = record
    pServerName: PWideChar;
    pPrinterName: PWideChar;
    // ... on tane daha
  end;

Buradan çıkan kural mekaniktir ve düşünmeden uygulanmaya değer: adı W ile biten her Win32 yapısı için her dize üyesinin geniş varyant olduğunu alan alan doğrulayın. ANSI ile geniş dünyayı karıştırmak ne derleyici tanısı ne çökme üretir, yalnızca sessiz kısaltma; aynı şey ANSI varyantları için de ters yönde geçerlidir

Asıl düşman çıplak istisna işleyicidir

Bu incelemelerin her biri aynı yapı tarafından yavaşlatıldı: her şeyi yakalayıp false dönüş değerine çeviren bir işleyici. Bir görüntü çözücünün çevresine yazmak makul bir şeydir; bozuk bir görüntü bir belge işini düşürmemelidir. Ama aynı zamanda ihtiyacınız olan tek bilgi parçasını silmeye yarayan bir aygıttır

Pratik tepki, işleyiciyi geçici olarak gürültülü kılmaktır. Çıplak işleyicinin içinden, bir debug koşulu altında, istisna sınıfını, mesajı ve geri izi dökmek, açıklanamayan bir null dönüşü konumlu adlandırılmış bir istisyona çevirir. Yukarıdaki üç durumdan ikisinde tek bu adım incelemeyi bitirdi; çünkü istisna, adı her şeyi söyleyen bir ikame yönteminde bir sıfıra bölme ya da bir erişim ihlaliydi

Bir ikame yolu benimsemek için kontrol listesi

Dört madde, karşılığını verdikleri sırayla. Bir ikame sınıfa çağrı yapmadan önce kullanacağınız yöntemleri okuyun ve her birinin gerçek bir gövdesi olduğunu doğrulayın; boş gövde bir uygulama ayrıntısı değildir, eksik bir özelliktir. Nötr değerler döndüren ikamelere atan ikameleri tercih edin ve bunu, bir fabrikanın artık meşru olarak hiçbir şey döndürebildiği yerlerdeki null denetimleriyle eşleştirin. Bir özelliği artefaktı inceleyerek doğrulayın, dönüş koduna bakarak değil; çünkü buradaki bütün hata modu boş bir artefakt üzerinde temiz bir dönüş kodudur. Bir belgenin gerçekte neler içerdiğinin bayt düzeyinde dökümü onu görmenin en hızlı yoludur ve dosya boyutu denetimi makalesi o araçları kapsar. Ve bir özelliğin yaşayabilir bir ikame uygulaması yoksa, etkilenen örnekleri gerçekten çalışan yola yönlendirin ve nedenini bir yorumla yazın; boş çıktıyı sessizce üreten bir örnek bırakmayın

Daha geniş nokta bir kitaplığın çok ötesine uygulanır. Koşullu ikinci bir uygulaması olan, mock katmanı olan, headless modu olan, platform uyum katmanı olan her kod tabanı üçüncü biçime açıktır. Bu kadar iyi saklanmasının nedeni, bir ekibin normalde güvendiği her kalite kapısının —dönüş kodları, hata kodları, istisnalar, çıkış durumları— bir durum kanalı olması ve üçüncü biçimin hepsini temiz tutmasıdır. Onu ele veren tek şey çıktıdır. Bu aynı zamanda, güvenilmeyen girdi işlerken durumlar yerine artefaktları denetlemenin —güvenilmeyen PDF ayrıştırma makalesinde anlatılır— ve birine güvenmek yerine çizilen çıktıyı motorlar arasında karşılaştırmanın —çok motorlu çizimde anlatılır— arkasındaki akıldır

PDFlibPas, Delphi, C++Builder ve Free Pascal için yerel bir Object Pascal PDF kitaplığıdır ve non-VCL yapılandırması, headless ve araç zincirleri arası derlemeleri mümkün kılan şeydir; güncel yapılandırma kapsamı losLab PDF Developer Library ürün sayfasında listelenir