Kada Delphi knjižnica stekne konfiguraciju izgradnje bez vizualnog okvira, zamjenske klase su mjesto gdje žive greške. Ne platforma, ne kompajler: namjenske zamjene. PDFlibPas ima grafički sloj koji pruža ekvivalente bitmape, canvasa, fonta, metafilea i pisača za izgradnje bez VCL-a, i portiranje na Free Pascal izvuklo je na površinu svaki način kvara koji zamjena može imati. Sortiraju se uredno po trošku dijagnoze, a poredak je suprotan onome što intuicija sugerira
Zamjena koja baca iznimku jeftina je za pronalaženje; iznimka imenuje metodu. Zamjena koja vraća prazne podatke skupa je, jer se kvar pokazuje nekoliko slojeva daleko od svojeg uzroka. Zamjena koja vraća uspjeh najgora je od svih, jer je povratni kôd valjan, kôd pogreške nula, nijedna iznimka se ne baca, a jedini dokaz da je nešto pošlo po zlu je u bajtovima koji su izašli
Oblik tri: valjan identifikator slike nad praznim XObjectom
Vektorski pretvarač metafilea bio je prazno tijelo procedure u ne-VCL konfiguraciji. Sve iznad njega nastavilo je raditi. Ulazne točke EMF uvoza i ulazna točka hvatanja canvasa odvile su se do kraja i vratile legalan identifikator slike, koji je pozivatelj zatim postavio na stranicu. U datoteku je sletio form XObject s duljinom sadržaja nula. Stranica se prikazala bijelom
Ništa nije javilo problem, a to uključuje i vlastiti demonstracijski program knjižnice za ovu značajku, koji je nacrtao praznu stranicu i nije primijetio. Nije bilo povratne vrijednosti kvara koju treba provjeriti, jer je niz poziva stvarno sve uspio; jedino što je bilo pogrešno bila je veličina proizvedenog toka. Dijagnosticiranje ove klase defekata znači postaviti drugo pitanje: ne "je li poziv pao" nego "je li artefakt uvjerljiv". Form XObject duljine nula, slika od nula piksela, stranica od nula bajtova sadržaja — to su tvrdnje koje to hvataju
Ispravak ima dvije polovice i druga se polovica lako zaboravi. Prvo, učinite da prazna implementacija baca iznimku, tako da kvar uopće ima kanal. Drugo, pretvorite tu iznimku u null rezultat u tvornici slika i dodajte null provjere na dva mjesta koja konzumiraju identifikator slike, jer će u protivnom "čisti kvar" prijeći ravno u kršenje pristupa kad stablo stranica dereferencira ništa. Začepčena verzija koja baca iznimku poboljšanje je samo ako su pozivatelji bili spremni za kvar koji prije nikad nisu mogli primiti
Oblik dva: prazni podaci, tri sloja od rušenja
Zamjenski metafile canvas nije popunio svoje fizičke dimenzije. Ta vrijednost dijeli se u izračun geometrije stranice, pa je izračun proizveo nulu, pa je izračun okvirnog okvira dijelio s nulom. Goli rukovatelj iznimkama progutao je to, tvornica slika vratila je null rezultat, a kršenje pristupa konačno se dogodilo u stablu stranica kada je null iskorišten. Tri sloja između uzroka i simptoma, s rukovateljem iznimkama u sredini koji briše dokaze
Ista jedinica imala je još dva primjera obrasca. Font klasa imala je prazna tijela Assign i konstruktora, što je važnije nego što izgleda jer je svojstvo fonta canvasa samo za čitanje: dodjeljivanje u njega jedini je način dostave fonta, pa prazna implementacija čini odabir fonta tiho ne djelotvornim, a tekst izlazi u onome što je bio zadan čim. I vrijednost piksela po palcu od nula učinila je da svaki pozivatelj koji dimenzionira canvas iz metrika fonta proizvede canvas nula puta nula, što daje praznu stranicu i povrat uspjeha
// Oblik koji treba tražiti u zamjenskoj jedinici: metoda koja ni
// ne baca iznimku ni ne radi ništa. Obje ove se kompajliraju i obje
// proizvode "uspjeh" bez ikakvog izlaza
procedure TMetafileCanvasStandIn.Create(...);
begin
// bez poziva inherited, bez inicijalizacije polja
end;
function TBitmapStandIn.LoadFromStream(Stream: TStream): Boolean;
begin
Result := True; // a bitmapa je i dalje prazna
end;
Široka struktura koja zadržava samo prvi znak
Ovo uopće nije problem zamjene, ali pripada istom katalogu jer je simptom jednako daleko od uzroka. Struktura nabiranja pisača deklarirana je sa svih dvanaest svojih znakovnih članova tipiziranih kao pokazivači na jednobajtne znakove, dok je funkcija koja je puni širokoznačna varijanta API-ja nabiranja
Veličine pokazivača identične su, pa je raspored strukture ispravan i ništa se ne ruši. Ono što se umjesto toga događa jest da čitanje UTF-16 niza kao jednobajtnog niza staje na prvom nula bajtu, koji je za svako ASCII ime pisača gornja polovica drugog znaka. Svako ime pisača vratilo se kao točno jedan znak. Nizvodno, provjera imena nije uspjela, stvaranje pisača nije uspjelo i ispis nije uspio za svaki stvarni pisač na stroju, a nijedan od tih simptoma ne upućuje na deklaraciju strukture
// Pogrešno: ispravna veličina, pogrešan tip elementa. Nema kompajlerske
// pogreške, nema rušenja, svaki niz skraćen na jedan znak
type
TPrinterInfo2Wrong = record
pServerName: PAnsiChar;
pPrinterName: PAnsiChar;
// ... deset dodatnih
end;
// Ispravno: struktura *W ima široke članove od početka do kraja
type
TPrinterInfo2W = record
pServerName: PWideChar;
pPrinterName: PWideChar;
// ... deset dodatnih
end;
Pravilo koje iz toga izlazi mehaničko je i vrijedi ga primijeniti bez razmišljanja: za svaku Win32 strukturu čije ime završava na W, provjerite da je svaki znakovni član široka varijanta, polje po polje. Miješanje ANSI i širokog svijeta ne proizvodi ni kompajlersku dijagnozu ni rušenje, samo tiho skraćivanje, a isto vrijedi obrnuto za ANSI varijante
Goli rukovatelj iznimkama stvarni je protivnik
Svako od ovih istraživanja usporavala je ista konstrukcija: rukovatelj koji hvata sve i pretvara u false povratnu vrijednost. To je razumna stvar napisati oko dekodera slika, jer oštećena slika ne bi trebala oboriti posao dokumenta. To je također sprava za brisanje jednog komada informacije koji vam treba
Praktičan odgovor jest učiniti da rukovatelj privremeno bude bučan. Izlijevanje klase iznimke, poruke i povratnog praćenja iznutra golog rukovatelja, pod uvjetnim izrazom za otklanjanje grešaka, pretvara neobjašnjen null povrat u imenovanu iznimku s lokacijom. U dva od tri gore navedena slučaja taj je jedan korak završio istraživanje, jer je iznimka bila dijeljenje s nulom ili kršenje pristupa u metodi zamjene čije je ime reklo sve
Popis provjera za usvajanje zamjenskog puta
Četiri stavke, u redoslijedu u kojem se isplate. Prije poziva u zamjensku klasu, pročitajte metode koje namjeravate koristiti i potvrdite da svaka ima stvarno tijelo; prazno tijelo nije detalj implementacije, to je nedostajuća značajka. Preferirajte zamjene koje bacaju iznimku nad zamjenama koje vraćaju neutralne vrijednosti, i uparite to s null provjerama na mjestima gdje tvornica sada legitimno može vratiti ništa. Provjerite značajku pregledom artefakta, ne povratnog kôda, jer je cijeli način kvara ovdje čist povratni kôd nad praznim artefaktom; rastava na razini bajtova onoga što dokument stvarno sadrži najbrži je način da se to vidi, a članak o reviziji veličine datoteke pokriva taj alat. I kada značajka nema održivu zamjensku implementaciju, usmerite pogođene uzorke na put koji radi i recite zašto u komentaru, umjesto ostavljanja demonstracije koja tiho proizvodi prazan izlaz
Šira točka primjenjuje se znatno izvan jedne knjižnice. Svaka baza kôda s uvjetnom drugom implementacijom, slojem lažnih objekata (mock), bezglavim načinom rada, platformskim šimom, izložena je obliku tri. Razlog što se tako dobro skriva jest to da je svaka provjera kvalitete na koju se tim obično oslanja — povratni kodovi, kodovi pogrešaka, iznimke, izlazna stanja — kanal stanja, a oblik tri drži sve njih čistima. Samo izlaz ga izdaje. To je također razlog provjeravanja artefakata umjesto stanja pri rukovanju nepouzdanim unosom, opisano u članku o raščlambi nepouzdanih PDF-ova, i razlog uspoređivanja prikazanog izlaza između motora umjesto vjerovanja jednome, opisano u prikazivanju s više motora
PDFlibPas je izvorna Object Pascal PDF knjižnica za Delphi, C++Builder i Free Pascal, a njezina ne-VCL konfiguracija je ono što čini mogućima bezglave i alatno-lančne izgradnje; trenutna pokrivenost konfiguracija navedena je na stranici proizvoda losLab PDF Developer Library