{
    Copyright (c) 2003-2006 by Peter Vreman, Florian Klaempfl, and Jonas Maebe

    This units contains support for Jasmin debug info generation

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 ****************************************************************************
}
unit dbgjasm;

{$i fpcdefs.inc}

interface

    uses
      cclasses,globtype,
      aasmbase,aasmtai,aasmdata,
      symbase,symconst,symtype,symdef,symsym,
      finput,
      DbgBase;

    type
      { TDebugInfoJasmin }

      TDebugInfoJasmin=class(TDebugInfo)
      protected
        fcurrprocstart,
        fcurrprocafterstart,
        fcurrprocend: tasmsymbol;

        procedure appendsym_localsym(list: TAsmList; sym: tabstractnormalvarsym; startlab: tasmsymbol);

        procedure appendsym_paravar(list:TAsmList;sym:tparavarsym);override;
        procedure appendsym_localvar(list:TAsmList;sym:tlocalvarsym);override;
        procedure beforeappenddef(list:TAsmList;def:tdef);override;
        procedure appendprocdef(list:TAsmList;def:tprocdef);override;
      public
        procedure inserttypeinfo;override;
        procedure insertlineinfo(list:TAsmList);override;
      end;

implementation

    uses
      sysutils,cutils,cfileutl,constexp,
      version,globals,verbose,systems,
      cpubase,cpuinfo,cgbase,paramgr,
      fmodule,
      defutil,symtable,symcpu,jvmdef,ppu
      ;

{****************************************************************************
                              TDebugInfoJasmin
****************************************************************************}

  procedure TDebugInfoJasmin.appendsym_localsym(list: TAsmList; sym: tabstractnormalvarsym; startlab: tasmsymbol);
    var
      jvar: tai_jvar;
      proc: tprocdef;
    begin
      if tdef(sym.owner.defowner).typ<>procdef then
        exit;
      if not(sym.localloc.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then
        exit;
      proc:=tprocdef(sym.owner.defowner);
      jvar:=tai_jvar.create(sym.localloc.reference.offset,jvmmangledbasename(sym,true),startlab,fcurrprocend);
      tcpuprocdef(proc).exprasmlist.InsertAfter(jvar,proc.procstarttai);
    end;


  procedure TDebugInfoJasmin.appendsym_paravar(list: TAsmList; sym: tparavarsym);
    begin
      appendsym_localsym(list,sym,fcurrprocstart);
    end;


  procedure TDebugInfoJasmin.appendsym_localvar(list: TAsmList; sym: tlocalvarsym);
    begin
      appendsym_localsym(list,sym,fcurrprocafterstart);
    end;


  procedure TDebugInfoJasmin.beforeappenddef(list: TAsmList; def: tdef);
    begin
    end;


  procedure TDebugInfoJasmin.appendprocdef(list: TAsmList; def: tprocdef);
    var
      procstartlabel,
      procendlabel,
      afterprocstartlabel : tasmlabel;
      hp,
      afterproccodestart  : tai;
      instrcount          : longint;
    begin
      { insert debug information for local variables and parameters, but only
        for routines implemented in the Pascal code }
      if not assigned(def.procstarttai) then
        exit;

      current_asmdata.getlabel(procstartlabel,alt_dbgtype);
      current_asmdata.getlabel(procendlabel,alt_dbgtype);
      tcpuprocdef(def).exprasmlist.insertafter(tai_label.create(procstartlabel),def.procstarttai);
      tcpuprocdef(def).exprasmlist.insertbefore(tai_label.create(procendlabel),def.procendtai);

      fcurrprocstart:=procstartlabel;
      { set the start label for local variables after the first instruction,
        because javac's code completion support assumes that all info at
        bytecode position 0 is for parameters }
      instrcount:=0;
      afterproccodestart:=def.procstarttai;
      while assigned(afterproccodestart.next) do
        begin
          afterproccodestart:=tai(afterproccodestart.next);
          if (afterproccodestart.typ=ait_instruction) then
            break;
        end;
      { must be followed by at least one more instruction }
      hp:=tai(afterproccodestart.next);
      while assigned(hp) do
        begin
          if hp.typ=ait_instruction then
            break;
          hp:=tai(hp.next);
        end;
      if assigned(hp) then
        begin
          current_asmdata.getlabel(afterprocstartlabel,alt_dbgtype);
          tcpuprocdef(def).exprasmlist.insertafter(tai_label.create(afterprocstartlabel),afterproccodestart);
          fcurrprocafterstart:=afterprocstartlabel;
        end
      else
        fcurrprocafterstart:=procstartlabel;
      fcurrprocend:=procendlabel;

      write_symtable_parasyms(list,def.paras);
      { not assigned for unit init }
      if assigned(def.localst) then
        write_symtable_syms(list,def.localst);
    end;


  procedure TDebugInfoJasmin.inserttypeinfo;
    begin
      { write all procedures and methods }
      if assigned(current_module.globalsymtable) then
        write_symtable_procdefs(nil,current_module.globalsymtable);
      if assigned(current_module.localsymtable) then
        write_symtable_procdefs(nil,current_module.localsymtable);
    end;

  procedure TDebugInfoJasmin.insertlineinfo(list: TAsmList);
    var
      currfileinfo,
      lastfileinfo : tfileposinfo;
      nolineinfolevel : Integer;
      currfuncname : pshortstring;
      hp : tai;
    begin
      FillChar(lastfileinfo,sizeof(lastfileinfo),0);
      hp:=Tai(list.first);
      nolineinfolevel:=0;
      while assigned(hp) do
        begin
          case hp.typ of
            ait_function_name :
              begin
                currfuncname:=tai_function_name(hp).funcname;
                list.concat(tai_comment.Create(strpnew('function: '+currfuncname^)));
              end;
            ait_force_line :
              begin
                lastfileinfo.line:=-1;
              end;
            ait_marker :
              begin
                case tai_marker(hp).kind of
                  mark_NoLineInfoStart:
                    inc(nolineinfolevel);
                  mark_NoLineInfoEnd:
                    dec(nolineinfolevel);
                end;
              end;
          end;

          { Java does not support multiple source files }
          if (hp.typ=ait_instruction) and
             (nolineinfolevel=0) and
             (tailineinfo(hp).fileinfo.fileindex=main_module.unit_index) then
            begin
              currfileinfo:=tailineinfo(hp).fileinfo;

              { line changed ? }
              if (lastfileinfo.line<>currfileinfo.line) and (currfileinfo.line<>0) then
                begin
                  { line directive }
                  list.insertbefore(tai_directive.Create(asd_jline,tostr(currfileinfo.line)),hp);
                end;
              lastfileinfo:=currfileinfo;
            end;

          hp:=tai(hp.next);
        end;
    end;


{****************************************************************************
****************************************************************************}
    const
      dbg_jasmin_info : tdbginfo =
         (
           id     : dbg_jasmin;
           idtxt  : 'JASMIN';
         );


initialization
  RegisterDebugInfo(dbg_jasmin_info,TDebugInfoJasmin);

end.
