Ciele
- Pochopiť princíp práce s lokálnymi premennými v JVM.
- Pochopiť implementáciu prekladu definície lokálnych premenných.
- Pochopiť princíp prekladu volania metódy.
Úvod
-
Na tomto cvičení sa zoznámite s prekladom dvoch veľmi dôležitých prvkov
programu v jazyku JMM --- definícia lokálnych premenných a volania metód.
Lokálne premenné umožňujú ukladať údaje, s ktorými sa pracuje v metóde. Pritom každé
volanie metódy má vlastnú oblasť pamäte pre ich uloženie. Táto
vlastnosť umožňuje napríklad rekurzívne volanie metódy.
Postup
-
Lokálne premenné sú spolu s parametrami metódy ukladané v pamäti v aktivačnom zázname metódy. Každá premenná má v tomto priestore svoju adresu. Virtuálne (nie statické) metódy majú v tejto oblasti pamäte navyše ešte aj referenciu na objekt, na ktorom sú volané. Spôsob priradenia adries premenných a parametrov metódy je znázornený na obrázku.Obr.: Spôsob uloženia lokálnych premenných a parametrov metódy.
-
Pre preklad práce s lokálnymi premennými je potrebné zaznamenať informácie o nich do špeciálnej údajovej štruktúry.
Následne je potrebné pri deklarácii metódy, jej parametrov a lokálnych premenných v nej, poznamenať všetky potrebné informácie do tabuľky premenných. Na to sa použijú pomocné funkcie/* Tabuľka lokálnych premenných aktuálne spracovávanej metódy */ struct { char *meno; /* Meno premennej */ char *sig; /* Signatúra typu */ int index; /* Pozícia premennej v pamäti */ } LP[20]; int ixlp=0; /* Prvá voľna pozícia v tabuľke */ int szlp=0; /* Veľkosť oblasti lokálnych premenných */
initLP
(pre inicializáciu tabuľky premenných),ulozLP
(pre uloženie informácií o premennej) aukoncLP
(pre zaznamenanie veľkosti pamäte potrebnej pre všetky premenné metódy do tabuľky tried). Princíp prekladu je v zjednodušenej podobe uvedený nižšie.
Uložené informácie je možné použiť pri preklade použitia premennej (načítanie jej hodnoty z pamäte) a priradenia hodnoty do premennej (kód je uvedený v zjednodušenej podobe):Metoda : ModClena TypDat ID { initLP($1 & MOD_STATIC); } LOZAT NepParametre POZAT { ulozMet($3, $6, $2, $1); } LZZAT Prikazy PZZAT { ukoncLP(); } Parametre : TypDat ID { ulozLP($2, $1); } | Parametre CIARKA TypDat ID { ulozLP($4, $3); } Prikaz : TypDat ZozIdent BCIARKA ZozIdent : ID { ulozLP($1, $<sig>0); } | ZozIdent CIARKA ID { ulozLP($3, $<sig>0); } TypDat : INT { $$ = "I"; } | DOUBLE { $$ = "D"; } | BOOLEAN { $$ = "Z"; } | TRIEDA { $$ = triedy[$1].sigTr; } | TRIEDA LHZAT PHZAT { $$ = "[triedy[$1].sigTr"; }
Primarny : ID { ii = najdiLP($1); $$.sig = LP[ii].sig; if ($$.sig[0] == 'L') $$.kod = "aload LP[ii].index"; if ($$.sig[0] == 'I') $$.kod = "iload LP[ii].index"; if ($$.sig[0] == 'Z') $$.kod = "iload LP[ii].index"; if ($$.sig[0] == 'D') $$.kod = "dload LP[ii].index"; } | ID ROVNE Vyraz { ii = najdiLP($1); $$.sig = LP[ii].sig; if (strcmp(LP[ii].sig, $3.sig) != 0) yyerror("Nekompatibilne priradenie"); if (LP[ii].sig[0] == 'L') $$.kod = $3.kod + "astore LP[ii].index" + "aload LP[ii].index"; if (LP[ii].sig[0] == 'I') $$.kod = $3.kod + "istore LP[ii].index" + "iload LP[ii].index"; if (LP[ii].sig[0] == 'D') $$.kod = $3.kod + "dstore LP[ii].index" + "dload LP[ii].index"; if (LP[ii].sig[0] == 'Z') $$.kod = $3.kod + "istore LP[ii].index" + "iload LP[ii].index"; }
Úloha: Oboznámte sa s implementáciou prekladu lokálnych premenných v prekladači. -
Pri preklade volania metód je potrebné poznať signatúru metódy, ktorá je uložená v tabuľke tried. Pre prehľadávanie tabuľky tried sú definované pomocné funkcie
najdiM
anajdiT
.Úloha: Oboznámte sa s implementáciou týchto funkcií v zdrojovom kóde prekladača.Pri volaní metód môže byť potrebné použiť referenciu na aktuálny objekt pomocou kľúčového slovathis
. Jeho preklad zabezpečí vloženie tejto referencie z pozície 0 v pamäti do zásobníka:
Samotné volanie metódy je pre bežné metódy prekladané do inštrukciePrimarny : THIS { $$.sig = triedy[ix_tr].sigTr; $$.kod = (char*) malloc(20); sprintf($$.kod, "\taload_0\n"); }
invokevirtual
, pre statické metódy –invokestatic
a pre konštruktory –invokespecial
. Pred vykonaním tejto inštrukcie je potrebné vyhodnotiť všetky argumenty metódy a zabezpečiť, aby ich hodnoty boli uložené v zásobníku. V prípade virtuálnej metódy musí byť v zásobníku aj referencia na objekt, na ktorom je metóda volaná. Navyše je potrebné zo signatúr argumentov zostaviť signatúru metódy. Môže totiž existovať niekoľko metód s rovnakým menom, ktoré sa líšia len signatúrou.Úloha: Oboznámte sa s implementáciou prekladu volania metód v kóde prekladača. Dôležité je prejsť si spracovanie symbolovMetVolanie
,NepArgumenty
aArgumenty
. Vyskúšajte prekladač na jednoduchom príklade (ahoj.jmm
) -
Úloha: Doplňte operátory inkrementácie a dekrementácie pre lokálne premenné (k neterminálnemu symbolu
Primarny
) v tvare:
Otestujte funkčnosť doplneného prekladača na príkladoch postupným odstraňovaním znakov komentára v súboreID ++ ++ ID ID -- -- ID ID += Vyraz ID -= Vyraz
ahoj.jmm
Zdroje
Primarny
, tak výsledkom ich vykonania musí byť zodpovedajúca hodnota na vrchole zásobníka.