Preklad lokálnych premenných a volania metód JMM

Ciele
  1. Pochopiť princíp práce s lokálnymi premennými v JVM.
  2. Pochopiť implementáciu prekladu definície lokálnych premenných.
  3. 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
  1. 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.
  2. Pre preklad práce s lokálnymi premennými je potrebné zaznamenať informácie o nich do špeciálnej údajovej štruktúry.
    
    /* 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 */
    
    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 initLP (pre inicializáciu tabuľky premenných), ulozLP (pre uloženie informácií o premennej) a ukoncLP (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.
    
    
    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"; }
    
    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):
    
    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.
  3. 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 a najdiT.
    Ú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 slova this. Jeho preklad zabezpečí vloženie tejto referencie z pozície 0 v pamäti do zásobníka:
    
    Primarny : THIS
               {
                 $$.sig = triedy[ix_tr].sigTr;
                 $$.kod = (char*) malloc(20);
                 sprintf($$.kod, "\taload_0\n");
               }
    
    Samotné volanie metódy je pre bežné metódy prekladané do inštrukcie 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 symbolov MetVolanie, NepArgumenty a Argumenty. Vyskúšajte prekladač na jednoduchom príklade (ahoj.jmm)
  4. Úloha: Doplňte operátory inkrementácie a dekrementácie pre lokálne premenné (k neterminálnemu symbolu Primarny) v tvare:
    
    ID ++
    ++ ID
    ID --
    -- ID
    ID += Vyraz
    ID -= Vyraz
    
    Otestujte funkčnosť doplneného prekladača na príkladoch postupným odstraňovaním znakov komentára v súbore ahoj.jmm
    Poznámka: Priorita doplnených operátorov by mala byť začlenená do existujúcej tabuľky priorít nasledovne:
    =  +=  -=
    || &&
    < <= > >= == !=
    + -
    * /
    ++ --
    .
    
    Vzhľadom na to, že tieto operátory majú byť pridané k neterminálnemu symbolu Primarny, tak výsledkom ich vykonania musí byť zodpovedajúca hodnota na vrchole zásobníka.
Zdroje
  1. Zdrojové kódy pre cvičenie 6
  2. Inštrukcie JVM
comments powered by Disqus