{
    Copyright (C) 2010 by Jonas Maebe

    This unit handles the temporary variables for the JVM

    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.

 ****************************************************************************
}
{
  This unit handles the temporary variables for the JVM.
}
unit tgcpu;

{$i fpcdefs.inc}

  interface

    uses
       globtype,
       aasmdata,
       cgutils,
       symtype,tgobj;

    type

       { ttgjvm }

       ttgjvm = class(ttgobj)
        protected
         procedure getimplicitobjtemp(list: TAsmList; def: tdef; temptype: ttemptype; out ref: treference);
         function getifspecialtemp(list: TAsmList; def: tdef; forcesize: asizeint; temptype: ttemptype; out ref: treference): boolean;
         procedure alloctemp(list: TAsmList; size: asizeint; alignment: shortint; temptype: ttemptype; def: tdef; fini: boolean; out ref: treference); override;
        public
         procedure setfirsttemp(l : asizeint); override;
         procedure getlocal(list: TAsmList; size: asizeint; alignment: shortint; def: tdef; var ref: treference); override;
         procedure gethltemp(list: TAsmList; def: tdef; forcesize: asizeint; temptype: ttemptype; out ref: treference); override;
         procedure gethltempmanaged(list: TAsmList; def: tdef; temptype: ttemptype; out ref: treference); override;
       end;

  implementation

    uses
       verbose,
       cgbase,
       symconst,symtable,symdef,symsym,symcpu,defutil,
       cpubase,aasmbase,aasmcpu,
       hlcgobj,hlcgcpu;


    { ttgjvm }

    procedure ttgjvm.getimplicitobjtemp(list: TAsmList; def: tdef; temptype: ttemptype; out ref: treference);
      var
        sym: tsym;
        pd: tprocdef;
      begin
        gettemp(list,java_jlobject.size,java_jlobject.alignment,temptype,ref);
        list.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(tabstractrecorddef(def).jvm_full_typename(true),AT_METADATA)));
        { the constructor doesn't return anything, so put a duplicate of the
          self pointer on the evaluation stack for use as function result
          after the constructor has run }
        list.concat(taicpu.op_none(a_dup));
        thlcgjvm(hlcg).incstack(list,2);
        { call the constructor }
        sym:=tsym(tabstractrecorddef(def).symtable.find('CREATE'));
        if assigned(sym) and
           (sym.typ=procsym) then
          begin
            pd:=tprocsym(sym).find_bytype_parameterless(potype_constructor);
            if not assigned(pd) then
              internalerror(2011032701);
          end
        else
          internalerror(2011060301);
        hlcg.a_call_name(list,pd,pd.mangledname,[],nil,false);
        thlcgjvm(hlcg).decstack(list,1);
        { store reference to instance }
        thlcgjvm(hlcg).a_load_stack_ref(list,java_jlobject,ref,0);
      end;


    function ttgjvm.getifspecialtemp(list: TAsmList; def: tdef; forcesize: asizeint; temptype: ttemptype; out ref: treference): boolean;
      var
        eledef: tdef;
        ndim: longint;
        sym: tsym;
        pd: tprocdef;
      begin
        result:=false;
        case def.typ of
          arraydef:
            begin
              if not is_dynamic_array(def) then
                begin
                  { allocate an array of the right size }
                  gettemp(list,java_jlobject.size,java_jlobject.alignment,temptype,ref);
                  ndim:=0;
                  eledef:=def;
                  repeat
                    if forcesize<>-1 then
                      thlcgjvm(hlcg).a_load_const_stack(list,s32inttype,forcesize div tarraydef(eledef).elesize,R_INTREGISTER)
                    else
                      thlcgjvm(hlcg).a_load_const_stack(list,s32inttype,tarraydef(eledef).elecount,R_INTREGISTER);
                    eledef:=tarraydef(eledef).elementdef;
                    inc(ndim);
                    forcesize:=-1;
                  until (eledef.typ<>arraydef) or
                        is_dynamic_array(eledef);
                  eledef:=tarraydef(def).elementdef;
                  thlcgjvm(hlcg).g_newarray(list,def,ndim);
                  thlcgjvm(hlcg).a_load_stack_ref(list,java_jlobject,ref,0);
                  result:=true;
                end;
            end;
          recorddef:
            begin
              getimplicitobjtemp(list,def,temptype,ref);
              result:=true;
            end;
          setdef:
            begin
              if tsetdef(def).elementdef.typ=enumdef then
                begin
                  { load enum class type }
                  list.concat(taicpu.op_sym(a_ldc,current_asmdata.RefAsmSymbol(tcpuenumdef(tenumdef(tsetdef(def).elementdef).getbasedef).classdef.jvm_full_typename(true),AT_METADATA)));
                  thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
                  { call tenumset.noneOf() class method }
                  sym:=tsym(tobjectdef(java_juenumset).symtable.find('NONEOF'));
                  if assigned(sym) and
                     (sym.typ=procsym) then
                    begin
                      if tprocsym(sym).procdeflist.Count<>1 then
                        internalerror(2011062801);
                      pd:=tprocdef(tprocsym(sym).procdeflist[0]);
                      hlcg.a_call_name(list,pd,pd.mangledname,[],nil,false);
                    end;
                  { static calls method replaces parameter with set instance
                    -> no change in stack height }
                end
              else
                begin
                  list.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(java_jubitset.jvm_full_typename(true),AT_METADATA)));
                  { the constructor doesn't return anything, so put a duplicate of the
                    self pointer on the evaluation stack for use as function result
                    after the constructor has run }
                  list.concat(taicpu.op_none(a_dup));
                  thlcgjvm(hlcg).incstack(list,2);
                  { call the constructor }
                  sym:=tsym(java_jubitset.symtable.find('CREATE'));
                  if assigned(sym) and
                     (sym.typ=procsym) then
                    begin
                      pd:=tprocsym(sym).find_bytype_parameterless(potype_constructor);
                      if not assigned(pd) then
                        internalerror(2011062802);
                    end
                  else
                    internalerror(2011062803);
                  hlcg.a_call_name(list,pd,pd.mangledname,[],nil,false);
                  { duplicate self pointer is removed }
                  thlcgjvm(hlcg).decstack(list,1);
                end;
              { store reference to instance }
              gettemp(list,java_jlobject.size,java_jlobject.alignment,temptype,ref);
              thlcgjvm(hlcg).a_load_stack_ref(list,java_jlobject,ref,0);
              result:=true;
            end;
          procvardef:
            begin
              if not tprocvardef(def).is_addressonly then
                begin
                  getimplicitobjtemp(list,tcpuprocvardef(def).classdef,temptype,ref);
                  result:=true;
                end;
            end;
          stringdef:
            begin
              if is_shortstring(def) then
                begin
                  gettemp(list,java_jlobject.size,java_jlobject.alignment,temptype,ref);
                  { add the maxlen parameter (s8inttype because parameters must
                    be sign extended) }
                  thlcgjvm(hlcg).a_load_const_stack(list,s8inttype,shortint(tstringdef(def).len),R_INTREGISTER);
                  { call the constructor }
                  sym:=tsym(tobjectdef(java_shortstring).symtable.find('CREATEEMPTY'));
                  if assigned(sym) and
                     (sym.typ=procsym) then
                    begin
                      if tprocsym(sym).procdeflist.Count<>1 then
                        internalerror(2011052404);
                      pd:=tprocdef(tprocsym(sym).procdeflist[0]);
                      hlcg.a_call_name(list,pd,pd.mangledname,[],nil,false);
                    end;
                  { static calls method replaces parameter with string instance
                    -> no change in stack height }
                  { store reference to instance }
                  thlcgjvm(hlcg).a_load_stack_ref(list,java_jlobject,ref,0);
                  result:=true;
                end;
            end;
          filedef:
            begin
              case tfiledef(def).filetyp of
                ft_text:
                  result:=getifspecialtemp(list,search_system_type('TEXTREC').typedef,forcesize,temptype,ref);
                ft_typed,
                ft_untyped:
                  result:=getifspecialtemp(list,search_system_type('FILEREC').typedef,forcesize,temptype,ref);
                else
                  internalerror(2015091405);
              end;
            end;
        end;
      end;


    procedure ttgjvm.alloctemp(list: TAsmList; size: asizeint; alignment: shortint; temptype: ttemptype; def: tdef; fini: boolean; out ref: treference);
      begin
        { the JVM only supports 1 slot (= 4 bytes in FPC) and 2 slot (= 8 bytes in
          FPC) temps on the stack. double and int64 are 2 slots, the rest is one slot.
          There are no problems with reusing the same slot for a value of a different
          type. There are no alignment requirements either. }
        if size<4 then
          size:=4;
        if not(size in [4,8]) then
          internalerror(2010121401);
        { don't pass on "def", since we don't care if a slot is used again for a
          different type }
        inherited alloctemp(list, size shr 2, 1, temptype, nil, false, ref);
      end;


    procedure ttgjvm.setfirsttemp(l: asizeint);
      begin
        firsttemp:=l;
        lasttemp:=l;
      end;


    procedure ttgjvm.getlocal(list: TAsmList; size: asizeint; alignment: shortint; def: tdef; var ref: treference);
      begin
        if not getifspecialtemp(list,def,size,tt_persistent,ref) then
          inherited;
      end;


    procedure ttgjvm.gethltemp(list: TAsmList; def: tdef; forcesize: asizeint; temptype: ttemptype; out ref: treference);
      begin
        if not getifspecialtemp(list,def,forcesize,temptype,ref) then
          inherited;
      end;

    procedure ttgjvm.gethltempmanaged(list: TAsmList; def: tdef; temptype: ttemptype; out ref: treference);
      begin
        gethltemp(list,def,def.size,temptype,ref);
      end;


begin
  tgobjclass:=ttgjvm;
end.
