Package org.luaj.vm2.compiler

Source Code of org.luaj.vm2.compiler.FuncState

/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.compiler;

import java.util.Hashtable;

import org.luaj.vm2.LuaBoolean;
import org.luaj.vm2.LuaDouble;
import org.luaj.vm2.LuaInteger;
import org.luaj.vm2.LocVars;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaNil;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.compiler.LexState.ConsControl;
import org.luaj.vm2.compiler.LexState.expdesc;


public class FuncState extends LuaC {
  class upvaldesc {
      short k;
      short info;
  };

  static class BlockCnt {
      BlockCnt previous;  /* chain */
      IntPtr breaklist = new IntPtr()/* list of jumps out of this loop */
      short nactvar;  /* # active locals outside the breakable structure */
      boolean upval;  /* true if some variable in the block is an upvalue */
      boolean isbreakable;  /* true if `block' is a loop */
    };
 
  Prototype f;  /* current function header */
//  LTable h;  /* table to find (and reuse) elements in `k' */
  Hashtable htable;  /* table to find (and reuse) elements in `k' */
  FuncState prev;  /* enclosing function */
  LexState ls;  /* lexical state */
  LuaC L;  /* compiler being invoked */
  BlockCnt bl;  /* chain of current blocks */
  int pc;  /* next position to code (equivalent to `ncode') */
  int lasttarget;   /* `pc' of last `jump target' */
  IntPtr jpc;  /* list of pending jumps to `pc' */
  int freereg;  /* first free register */
  int nk;  /* number of elements in `k' */
  int np;  /* number of elements in `p' */
  short nlocvars;  /* number of elements in `locvars' */
  short nactvar;  /* number of active local variables */
  upvaldesc upvalues[] = new upvaldesc[LUAI_MAXUPVALUES]/* upvalues */
  short actvar[] = new short[LUAI_MAXVARS]/* declared-variable stack */
 
  FuncState() {
  }
 
 
  // =============================================================
  // from lcode.h
  // =============================================================

  InstructionPtr getcodePtr(expdesc e) {
    return new InstructionPtr( f.code, e.u.s.info );
  }

  int getcode(expdesc e) {
    return f.code[e.u.s.info];
  }

  int codeAsBx(int o, int A, int sBx) {
    return codeABx(o,A,sBx+MAXARG_sBx);
  }

  void setmultret(expdesc e) {
    setreturns(e, LUA_MULTRET);
  }

 
  // =============================================================
  // from lparser.c
  // =============================================================

  LocVars getlocvar(int i) {
    return f.locvars[actvar[i]];
  }

  void checklimit(int v, int l, String msg) {
    if ( v > l )
      errorlimit( l, msg );
  }
 
  void errorlimit (int limit, String what) {
      String msg = (f.linedefined == 0) ?
          L.pushfstring("main function has more than "+limit+" "+what) :
          L.pushfstring("function at line "+f.linedefined+" has more than "+limit+" "+what);
        ls.lexerror(msg, 0);
  }


  int indexupvalue(LuaString name, expdesc v) {
    int i;
    for (i = 0; i < f.nups; i++) {
      if (upvalues[i].k == v.k && upvalues[i].info == v.u.s.info) {
        _assert(f.upvalues[i] == name);
        return i;
      }
    }
    /* new one */
    checklimit(f.nups + 1, LUAI_MAXUPVALUES, "upvalues");
    if ( f.upvalues == null || f.nups + 1 > f.upvalues.length)
      f.upvalues = realloc( f.upvalues, f.nups*2+1 );
    f.upvalues[f.nups] = name;
    _assert (v.k == LexState.VLOCAL || v.k == LexState.VUPVAL);
    upvalues[f.nups] = new upvaldesc();
    upvalues[f.nups].k = (short) (v.k);
    upvalues[f.nups].info = (short) (v.u.s.info);
    return f.nups++;
  }
   
  int searchvar(LuaString n) {
    int i;
    for (i = nactvar - 1; i >= 0; i--) {
      if (n == getlocvar(i).varname)
        return i;
    }
    return -1; /* not found */
  }
   
  void markupval(int level) {
    BlockCnt bl = this.bl;
    while (bl != null && bl.nactvar > level)
      bl = bl.previous;
    if (bl != null)
      bl.upval = true;
  }
   
  int singlevaraux(LuaString n, expdesc var, int base) {
    int v = searchvar(n); /* look up at current level */
    if (v >= 0) {
      var.init(LexState.VLOCAL, v);
      if (base == 0)
        markupval(v); /* local will be used as an upval */
      return LexState.VLOCAL;
    } else { /* not found at current level; try upper one */
      if (prev == null) { /* no more levels? */
        /* default is global variable */
        var.init(LexState.VGLOBAL, NO_REG);
        return LexState.VGLOBAL;
      }
      if (prev.singlevaraux(n, var, 0) == LexState.VGLOBAL)
        return LexState.VGLOBAL;
      var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */
      var.k = LexState.VUPVAL; /* upvalue in this level */
      return LexState.VUPVAL;
    }
  }
 
  void enterblock (BlockCnt bl, boolean isbreakable) {
    bl.breaklist.i = LexState.NO_JUMP;
    bl.isbreakable = isbreakable;
    bl.nactvar = this.nactvar;
    bl.upval = false;
    bl.previous = this.bl;
    this.bl = bl;
    _assert(this.freereg == this.nactvar);
  }

  //
//  void leaveblock (FuncState *fs) {
//    BlockCnt *bl = this.bl;
//    this.bl = bl.previous;
//    removevars(this.ls, bl.nactvar);
//    if (bl.upval)
//      this.codeABC(OP_CLOSE, bl.nactvar, 0, 0);
//    /* a block either controls scope or breaks (never both) */
//    assert(!bl.isbreakable || !bl.upval);
//    assert(bl.nactvar == this.nactvar);
//    this.freereg = this.nactvar;  /* free registers */
//    this.patchtohere(bl.breaklist);
//  }
 
  void leaveblock() {
    BlockCnt bl = this.bl;
    this.bl = bl.previous;
    ls.removevars(bl.nactvar);
    if (bl.upval)
      this.codeABC(OP_CLOSE, bl.nactvar, 0, 0);
    /* a block either controls scope or breaks (never both) */
    _assert (!bl.isbreakable || !bl.upval);
    _assert (bl.nactvar == this.nactvar);
    this.freereg = this.nactvar; /* free registers */
    this.patchtohere(bl.breaklist.i);
  }

  void closelistfield(ConsControl cc) {
    if (cc.v.k == LexState.VVOID)
      return; /* there is no list item */
    this.exp2nextreg(cc.v);
    cc.v.k = LexState.VVOID;
    if (cc.tostore == LFIELDS_PER_FLUSH) {
      this.setlist(cc.t.u.s.info, cc.na, cc.tostore); /* flush */
      cc.tostore = 0; /* no more items pending */
    }
  }

  boolean hasmultret(int k) {
    return ((k) == LexState.VCALL || (k) == LexState.VVARARG);
  }

  void lastlistfield (ConsControl cc) {
    if (cc.tostore == 0) return;
    if (hasmultret(cc.v.k)) {
        this.setmultret(cc.v);
        this.setlist(cc.t.u.s.info, cc.na, LUA_MULTRET);
        cc.na--;  /** do not count last expression (unknown number of elements) */
    }
    else {
        if (cc.v.k != LexState.VVOID)
          this.exp2nextreg(cc.v);
        this.setlist(cc.t.u.s.info, cc.na, cc.tostore);
    }
  }
 
 
 
  // =============================================================
  // from lcode.c
  // =============================================================

  void nil(int from, int n) {
    InstructionPtr previous;
    if (this.pc > this.lasttarget) { /* no jumps to current position? */
      if (this.pc == 0) { /* function start? */
        if (from >= this.nactvar)
          return; /* positions are already clean */
      } else {
        previous = new InstructionPtr(this.f.code, this.pc - 1);
        if (GET_OPCODE(previous.get()) == OP_LOADNIL) {
          int pfrom = GETARG_A(previous.get());
          int pto = GETARG_B(previous.get());
          if (pfrom <= from && from <= pto + 1) { /* can connect both? */
            if (from + n - 1 > pto)
              SETARG_B(previous, from + n - 1);
            return;
          }
        }
      }
    }
    /* else no optimization */
    this.codeABC(OP_LOADNIL, from, from + n - 1, 0);
  }


  int jump() {
    int jpc = this.jpc.i; /* save list of jumps to here */
    this.jpc.i = LexState.NO_JUMP;
    IntPtr j = new IntPtr(this.codeAsBx(OP_JMP, 0, LexState.NO_JUMP));
    this.concat(j, jpc); /* keep them on hold */
    return j.i;
  }

  void ret(int first, int nret) {
    this.codeABC(OP_RETURN, first, nret + 1, 0);
  }

  int condjump(int /* OpCode */op, int A, int B, int C) {
    this.codeABC(op, A, B, C);
    return this.jump();
  }

  void fixjump(int pc, int dest) {
    InstructionPtr jmp = new InstructionPtr(this.f.code, pc);
    int offset = dest - (pc + 1);
    _assert (dest != LexState.NO_JUMP);
    if (Math.abs(offset) > MAXARG_sBx)
      ls.syntaxerror("control structure too long");
    SETARG_sBx(jmp, offset);
  }


  /*
   * * returns current `pc' and marks it as a jump target (to avoid wrong *
   * optimizations with consecutive instructions not in the same basic block).
   */
  int getlabel() {
    this.lasttarget = this.pc;
    return this.pc;
  }


  int getjump(int pc) {
    int offset = GETARG_sBx(this.f.code[pc]);
    /* point to itself represents end of list */
    if (offset == LexState.NO_JUMP)
      /* end of list */
      return LexState.NO_JUMP;
    else
      /* turn offset into absolute position */
      return (pc + 1) + offset;
  }


  InstructionPtr getjumpcontrol(int pc) {
    InstructionPtr pi = new InstructionPtr(this.f.code, pc);
    if (pc >= 1 && testTMode(GET_OPCODE(pi.code[pi.idx - 1])))
      return new InstructionPtr(pi.code, pi.idx - 1);
    else
      return pi;
  }


  /*
   * * check whether list has any jump that do not produce a value * (or
   * produce an inverted value)
   */
  boolean need_value(int list) {
    for (; list != LexState.NO_JUMP; list = this.getjump(list)) {
      int i = this.getjumpcontrol(list).get();
      if (GET_OPCODE(i) != OP_TESTSET)
        return true;
    }
    return false; /* not found */
  }


  boolean patchtestreg(int node, int reg) {
    InstructionPtr i = this.getjumpcontrol(node);
    if (GET_OPCODE(i.get()) != OP_TESTSET)
      /* cannot patch other instructions */
      return false;
    if (reg != NO_REG && reg != GETARG_B(i.get()))
      SETARG_A(i, reg);
    else
      /* no register to put value or register already has the value */
      i.set(CREATE_ABC(OP_TEST, GETARG_B(i.get()), 0, Lua.GETARG_C(i.get())));

    return true;
  }


  void removevalues(int list) {
    for (; list != LexState.NO_JUMP; list = this.getjump(list))
      this.patchtestreg(list, NO_REG);
  }

  void patchlistaux(int list, int vtarget, int reg, int dtarget) {
    while (list != LexState.NO_JUMP) {
      int next = this.getjump(list);
      if (this.patchtestreg(list, reg))
        this.fixjump(list, vtarget);
      else
        this.fixjump(list, dtarget); /* jump to default target */
      list = next;
    }
  }

  void dischargejpc() {
    this.patchlistaux(this.jpc.i, this.pc, NO_REG, this.pc);
    this.jpc.i = LexState.NO_JUMP;
  }

  void patchlist(int list, int target) {
    if (target == this.pc)
      this.patchtohere(list);
    else {
      _assert (target < this.pc);
      this.patchlistaux(list, target, NO_REG, target);
    }
  }

  void patchtohere(int list) {
    this.getlabel();
    this.concat(this.jpc, list);
  }

  void concat(IntPtr l1, int l2) {
    if (l2 == LexState.NO_JUMP)
      return;
    if (l1.i == LexState.NO_JUMP)
      l1.i = l2;
    else {
      int list = l1.i;
      int next;
      while ((next = this.getjump(list)) != LexState.NO_JUMP)
        /* find last element */
        list = next;
      this.fixjump(list, l2);
    }
  }

  void checkstack(int n) {
    int newstack = this.freereg + n;
    if (newstack > this.f.maxstacksize) {
      if (newstack >= MAXSTACK)
        ls.syntaxerror("function or expression too complex");
      this.f.maxstacksize = newstack;
    }
  }

  void reserveregs(int n) {
    this.checkstack(n);
    this.freereg += n;
  }

  void freereg(int reg) {
    if (!ISK(reg) && reg >= this.nactvar) {
      this.freereg--;
      _assert (reg == this.freereg);
    }
  }

  void freeexp(expdesc e) {
    if (e.k == LexState.VNONRELOC)
      this.freereg(e.u.s.info);
  }

  int addk(LuaValue v) {
    int idx;
    if (this.htable.containsKey(v)) {
      idx = ((Integer) htable.get(v)).intValue();
    } else {
      idx = this.nk;
      this.htable.put(v, new Integer(idx));
      final Prototype f = this.f;
      if (f.k == null || nk + 1 >= f.k.length)
        f.k = realloc( f.k, nk*2 + 1 );
      f.k[this.nk++] = v;
    }
    return idx;
  }

  int stringK(LuaString s) {
    return this.addk(s);
  }

  int numberK(LuaValue r) {
    if ( r instanceof LuaDouble ) {
      double d = r.todouble();
      int i = (int) d;
      if ( d == (double) i )
        r = LuaInteger.valueOf(i);
    }
    return this.addk(r);
  }

  int boolK(boolean b) {
    return this.addk((b ? LuaValue.TRUE : LuaValue.FALSE));
  }

  int nilK() {
    return this.addk(LuaValue.NIL);
  }

  void setreturns(expdesc e, int nresults) {
    if (e.k == LexState.VCALL) { /* expression is an open function call? */
      SETARG_C(this.getcodePtr(e), nresults + 1);
    } else if (e.k == LexState.VVARARG) {
      SETARG_B(this.getcodePtr(e), nresults + 1);
      SETARG_A(this.getcodePtr(e), this.freereg);
      this.reserveregs(1);
    }
  }

  void setoneret(expdesc e) {
    if (e.k == LexState.VCALL) { /* expression is an open function call? */
      e.k = LexState.VNONRELOC;
      e.u.s.info = GETARG_A(this.getcode(e));
    } else if (e.k == LexState.VVARARG) {
      SETARG_B(this.getcodePtr(e), 2);
      e.k = LexState.VRELOCABLE; /* can relocate its simple result */
    }
  }

  void dischargevars(expdesc e) {
    switch (e.k) {
    case LexState.VLOCAL: {
      e.k = LexState.VNONRELOC;
      break;
    }
    case LexState.VUPVAL: {
      e.u.s.info = this.codeABC(OP_GETUPVAL, 0, e.u.s.info, 0);
      e.k = LexState.VRELOCABLE;
      break;
    }
    case LexState.VGLOBAL: {
      e.u.s.info = this.codeABx(OP_GETGLOBAL, 0, e.u.s.info);
      e.k = LexState.VRELOCABLE;
      break;
    }
    case LexState.VINDEXED: {
      this.freereg(e.u.s.aux);
      this.freereg(e.u.s.info);
      e.u.s.info = this
          .codeABC(OP_GETTABLE, 0, e.u.s.info, e.u.s.aux);
      e.k = LexState.VRELOCABLE;
      break;
    }
    case LexState.VVARARG:
    case LexState.VCALL: {
      this.setoneret(e);
      break;
    }
    default:
      break; /* there is one value available (somewhere) */
    }
  }

  int code_label(int A, int b, int jump) {
    this.getlabel(); /* those instructions may be jump targets */
    return this.codeABC(OP_LOADBOOL, A, b, jump);
  }

  void discharge2reg(expdesc e, int reg) {
    this.dischargevars(e);
    switch (e.k) {
    case LexState.VNIL: {
      this.nil(reg, 1);
      break;
    }
    case LexState.VFALSE:
    case LexState.VTRUE: {
      this.codeABC(OP_LOADBOOL, reg, (e.k == LexState.VTRUE ? 1 : 0),
          0);
      break;
    }
    case LexState.VK: {
      this.codeABx(OP_LOADK, reg, e.u.s.info);
      break;
    }
    case LexState.VKNUM: {
      this.codeABx(OP_LOADK, reg, this.numberK(e.u.nval()));
      break;
    }
    case LexState.VRELOCABLE: {
      InstructionPtr pc = this.getcodePtr(e);
      SETARG_A(pc, reg);
      break;
    }
    case LexState.VNONRELOC: {
      if (reg != e.u.s.info)
        this.codeABC(OP_MOVE, reg, e.u.s.info, 0);
      break;
    }
    default: {
      _assert (e.k == LexState.VVOID || e.k == LexState.VJMP);
      return; /* nothing to do... */
    }
    }
    e.u.s.info = reg;
    e.k = LexState.VNONRELOC;
  }

  void discharge2anyreg(expdesc e) {
    if (e.k != LexState.VNONRELOC) {
      this.reserveregs(1);
      this.discharge2reg(e, this.freereg - 1);
    }
  }

  void exp2reg(expdesc e, int reg) {
    this.discharge2reg(e, reg);
    if (e.k == LexState.VJMP)
      this.concat(e.t, e.u.s.info); /* put this jump in `t' list */
    if (e.hasjumps()) {
      int _final; /* position after whole expression */
      int p_f = LexState.NO_JUMP; /* position of an eventual LOAD false */
      int p_t = LexState.NO_JUMP; /* position of an eventual LOAD true */
      if (this.need_value(e.t.i) || this.need_value(e.f.i)) {
        int fj = (e.k == LexState.VJMP) ? LexState.NO_JUMP : this
            .jump();
        p_f = this.code_label(reg, 0, 1);
        p_t = this.code_label(reg, 1, 0);
        this.patchtohere(fj);
      }
      _final = this.getlabel();
      this.patchlistaux(e.f.i, _final, reg, p_f);
      this.patchlistaux(e.t.i, _final, reg, p_t);
    }
    e.f.i = e.t.i = LexState.NO_JUMP;
    e.u.s.info = reg;
    e.k = LexState.VNONRELOC;
  }

  void exp2nextreg(expdesc e) {
    this.dischargevars(e);
    this.freeexp(e);
    this.reserveregs(1);
    this.exp2reg(e, this.freereg - 1);
  }

  int exp2anyreg(expdesc e) {
    this.dischargevars(e);
    if (e.k == LexState.VNONRELOC) {
      if (!e.hasjumps())
        return e.u.s.info; /* exp is already in a register */
      if (e.u.s.info >= this.nactvar) { /* reg. is not a local? */
        this.exp2reg(e, e.u.s.info); /* put value on it */
        return e.u.s.info;
      }
    }
    this.exp2nextreg(e); /* default */
    return e.u.s.info;
  }

  void exp2val(expdesc e) {
    if (e.hasjumps())
      this.exp2anyreg(e);
    else
      this.dischargevars(e);
  }

  int exp2RK(expdesc e) {
    this.exp2val(e);
    switch (e.k) {
    case LexState.VKNUM:
    case LexState.VTRUE:
    case LexState.VFALSE:
    case LexState.VNIL: {
      if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */
        e.u.s.info = (e.k == LexState.VNIL) ? this.nilK()
            : (e.k == LexState.VKNUM) ? this.numberK(e.u.nval())
                : this.boolK((e.k == LexState.VTRUE));
        e.k = LexState.VK;
        return RKASK(e.u.s.info);
      } else
        break;
    }
    case LexState.VK: {
      if (e.u.s.info <= MAXINDEXRK) /* constant fit in argC? */
        return RKASK(e.u.s.info);
      else
        break;
    }
    default:
      break;
    }
    /* not a constant in the right range: put it in a register */
    return this.exp2anyreg(e);
  }

  void storevar(expdesc var, expdesc ex) {
    switch (var.k) {
    case LexState.VLOCAL: {
      this.freeexp(ex);
      this.exp2reg(ex, var.u.s.info);
      return;
    }
    case LexState.VUPVAL: {
      int e = this.exp2anyreg(ex);
      this.codeABC(OP_SETUPVAL, e, var.u.s.info, 0);
      break;
    }
    case LexState.VGLOBAL: {
      int e = this.exp2anyreg(ex);
      this.codeABx(OP_SETGLOBAL, e, var.u.s.info);
      break;
    }
    case LexState.VINDEXED: {
      int e = this.exp2RK(ex);
      this.codeABC(OP_SETTABLE, var.u.s.info, var.u.s.aux, e);
      break;
    }
    default: {
      _assert (false); /* invalid var kind to store */
      break;
    }
    }
    this.freeexp(ex);
  }

  void self(expdesc e, expdesc key) {
    int func;
    this.exp2anyreg(e);
    this.freeexp(e);
    func = this.freereg;
    this.reserveregs(2);
    this.codeABC(OP_SELF, func, e.u.s.info, this.exp2RK(key));
    this.freeexp(key);
    e.u.s.info = func;
    e.k = LexState.VNONRELOC;
  }

  void invertjump(expdesc e) {
    InstructionPtr pc = this.getjumpcontrol(e.u.s.info);
    _assert (testTMode(GET_OPCODE(pc.get()))
        && GET_OPCODE(pc.get()) != OP_TESTSET && Lua
        .GET_OPCODE(pc.get()) != OP_TEST);
    // SETARG_A(pc, !(GETARG_A(pc.get())));
    int a = GETARG_A(pc.get());
    int nota = (a!=0? 0: 1);
    SETARG_A(pc, nota);
  }

  int jumponcond(expdesc e, int cond) {
    if (e.k == LexState.VRELOCABLE) {
      int ie = this.getcode(e);
      if (GET_OPCODE(ie) == OP_NOT) {
        this.pc--; /* remove previous OP_NOT */
        return this.condjump(OP_TEST, GETARG_B(ie), 0, (cond!=0? 0: 1));
      }
      /* else go through */
    }
    this.discharge2anyreg(e);
    this.freeexp(e);
    return this.condjump(OP_TESTSET, NO_REG, e.u.s.info, cond);
  }

  void goiftrue(expdesc e) {
    int pc; /* pc of last jump */
    this.dischargevars(e);
    switch (e.k) {
    case LexState.VK:
    case LexState.VKNUM:
    case LexState.VTRUE: {
      pc = LexState.NO_JUMP; /* always true; do nothing */
      break;
    }
    case LexState.VFALSE: {
      pc = this.jump(); /* always jump */
      break;
    }
    case LexState.VJMP: {
      this.invertjump(e);
      pc = e.u.s.info;
      break;
    }
    default: {
      pc = this.jumponcond(e, 0);
      break;
    }
    }
    this.concat(e.f, pc); /* insert last jump in `f' list */
    this.patchtohere(e.t.i);
    e.t.i = LexState.NO_JUMP;
  }

  void goiffalse(expdesc e) {
    int pc; /* pc of last jump */
    this.dischargevars(e);
    switch (e.k) {
    case LexState.VNIL:
    case LexState.VFALSE: {
      pc = LexState.NO_JUMP; /* always false; do nothing */
      break;
    }
    case LexState.VTRUE: {
      pc = this.jump(); /* always jump */
      break;
    }
    case LexState.VJMP: {
      pc = e.u.s.info;
      break;
    }
    default: {
      pc = this.jumponcond(e, 1);
      break;
    }
    }
    this.concat(e.t, pc); /* insert last jump in `t' list */
    this.patchtohere(e.f.i);
    e.f.i = LexState.NO_JUMP;
  }

  void codenot(expdesc e) {
    this.dischargevars(e);
    switch (e.k) {
    case LexState.VNIL:
    case LexState.VFALSE: {
      e.k = LexState.VTRUE;
      break;
    }
    case LexState.VK:
    case LexState.VKNUM:
    case LexState.VTRUE: {
      e.k = LexState.VFALSE;
      break;
    }
    case LexState.VJMP: {
      this.invertjump(e);
      break;
    }
    case LexState.VRELOCABLE:
    case LexState.VNONRELOC: {
      this.discharge2anyreg(e);
      this.freeexp(e);
      e.u.s.info = this.codeABC(OP_NOT, 0, e.u.s.info, 0);
      e.k = LexState.VRELOCABLE;
      break;
    }
    default: {
      _assert (false); /* cannot happen */
      break;
    }
    }
    /* interchange true and false lists */
    {
      int temp = e.f.i;
      e.f.i = e.t.i;
      e.t.i = temp;
    }
    this.removevalues(e.f.i);
    this.removevalues(e.t.i);
  }

  void indexed(expdesc t, expdesc k) {
    t.u.s.aux = this.exp2RK(k);
    t.k = LexState.VINDEXED;
  }

  boolean constfolding(int op, expdesc e1, expdesc e2) {
    LuaValue v1, v2, r;
    if (!e1.isnumeral() || !e2.isnumeral())
      return false;
    v1 = e1.u.nval();
    v2 = e2.u.nval();
    switch (op) {
    case OP_ADD:
      r = v1.add(v2);
      break;
    case OP_SUB:
      r = v1.sub(v2);
      break;
    case OP_MUL:
      r = v1.mul(v2);
      break;
    case OP_DIV:
      r = v1.div(v2);
      break;
    case OP_MOD:
      r = v1.mod(v2);
      break;
    case OP_POW:
      r = v1.pow(v2);
      break;
    case OP_UNM:
      r = v1.neg();
      break;
    case OP_LEN:
      // r = v1.len();
      // break;
      return false; /* no constant folding for 'len' */
    default:
      _assert (false);
      r = null;
      break;
    }
    if ( Double.isNaN(r.todouble()) )
      return false; /* do not attempt to produce NaN */
    e1.u.setNval( r );
    return true;
  }

  void codearith(int op, expdesc e1, expdesc e2) {
    if (constfolding(op, e1, e2))
      return;
    else {
      int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2)
          : 0;
      int o1 = this.exp2RK(e1);
      if (o1 > o2) {
        this.freeexp(e1);
        this.freeexp(e2);
      } else {
        this.freeexp(e2);
        this.freeexp(e1);
      }
      e1.u.s.info = this.codeABC(op, 0, o1, o2);
      e1.k = LexState.VRELOCABLE;
    }
  }

  void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) {
    int o1 = this.exp2RK(e1);
    int o2 = this.exp2RK(e2);
    this.freeexp(e2);
    this.freeexp(e1);
    if (cond == 0 && op != OP_EQ) {
      int temp; /* exchange args to replace by `<' or `<=' */
      temp = o1;
      o1 = o2;
      o2 = temp; /* o1 <==> o2 */
      cond = 1;
    }
    e1.u.s.info = this.condjump(op, cond, o1, o2);
    e1.k = LexState.VJMP;
  }

  void prefix(int /* UnOpr */op, expdesc e) {
    expdesc e2 = new expdesc();
    e2.init(LexState.VKNUM, 0);
    switch (op) {
    case LexState.OPR_MINUS: {
      if (e.k == LexState.VK)
        this.exp2anyreg(e); /* cannot operate on non-numeric constants */
      this.codearith(OP_UNM, e, e2);
      break;
    }
    case LexState.OPR_NOT:
      this.codenot(e);
      break;
    case LexState.OPR_LEN: {
      this.exp2anyreg(e); /* cannot operate on constants */
      this.codearith(OP_LEN, e, e2);
      break;
    }
    default:
      _assert (false);
    }
  }

  void infix(int /* BinOpr */op, expdesc v) {
    switch (op) {
    case LexState.OPR_AND: {
      this.goiftrue(v);
      break;
    }
    case LexState.OPR_OR: {
      this.goiffalse(v);
      break;
    }
    case LexState.OPR_CONCAT: {
      this.exp2nextreg(v); /* operand must be on the `stack' */
      break;
    }
    case LexState.OPR_ADD:
    case LexState.OPR_SUB:
    case LexState.OPR_MUL:
    case LexState.OPR_DIV:
    case LexState.OPR_MOD:
    case LexState.OPR_POW: {
      if (!v.isnumeral())
        this.exp2RK(v);
      break;
    }
    default: {
      this.exp2RK(v);
      break;
    }
    }
  }


  void posfix(int op, expdesc e1, expdesc e2) {
    switch (op) {
    case LexState.OPR_AND: {
      _assert (e1.t.i == LexState.NO_JUMP); /* list must be closed */
      this.dischargevars(e2);
      this.concat(e2.f, e1.f.i);
      // *e1 = *e2;
      e1.setvalue(e2);
      break;
    }
    case LexState.OPR_OR: {
      _assert (e1.f.i == LexState.NO_JUMP); /* list must be closed */
      this.dischargevars(e2);
      this.concat(e2.t, e1.t.i);
      // *e1 = *e2;
      e1.setvalue(e2);
      break;
    }
    case LexState.OPR_CONCAT: {
      this.exp2val(e2);
      if (e2.k == LexState.VRELOCABLE
          && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) {
        _assert (e1.u.s.info == GETARG_B(this.getcode(e2)) - 1);
        this.freeexp(e1);
        SETARG_B(this.getcodePtr(e2), e1.u.s.info);
        e1.k = LexState.VRELOCABLE;
        e1.u.s.info = e2.u.s.info;
      } else {
        this.exp2nextreg(e2); /* operand must be on the 'stack' */
        this.codearith(OP_CONCAT, e1, e2);
      }
      break;
    }
    case LexState.OPR_ADD:
      this.codearith(OP_ADD, e1, e2);
      break;
    case LexState.OPR_SUB:
      this.codearith(OP_SUB, e1, e2);
      break;
    case LexState.OPR_MUL:
      this.codearith(OP_MUL, e1, e2);
      break;
    case LexState.OPR_DIV:
      this.codearith(OP_DIV, e1, e2);
      break;
    case LexState.OPR_MOD:
      this.codearith(OP_MOD, e1, e2);
      break;
    case LexState.OPR_POW:
      this.codearith(OP_POW, e1, e2);
      break;
    case LexState.OPR_EQ:
      this.codecomp(OP_EQ, 1, e1, e2);
      break;
    case LexState.OPR_NE:
      this.codecomp(OP_EQ, 0, e1, e2);
      break;
    case LexState.OPR_LT:
      this.codecomp(OP_LT, 1, e1, e2);
      break;
    case LexState.OPR_LE:
      this.codecomp(OP_LE, 1, e1, e2);
      break;
    case LexState.OPR_GT:
      this.codecomp(OP_LT, 0, e1, e2);
      break;
    case LexState.OPR_GE:
      this.codecomp(OP_LE, 0, e1, e2);
      break;
    default:
      _assert (false);
    }
  }


  void fixline(int line) {
    this.f.lineinfo[this.pc - 1] = line;
  }


  int code(int instruction, int line) {
    Prototype f = this.f;
    this.dischargejpc(); /* `pc' will change */
    /* put new instruction in code array */
    if (f.code == null || this.pc + 1 > f.code.length)
      f.code = LuaC.realloc(f.code, this.pc * 2 + 1);
    f.code[this.pc] = instruction;
    /* save corresponding line information */
    if (f.lineinfo == null || this.pc + 1 > f.lineinfo.length)
      f.lineinfo = LuaC.realloc(f.lineinfo,
          this.pc * 2 + 1);
    f.lineinfo[this.pc] = line;
    return this.pc++;
  }


  int codeABC(int o, int a, int b, int c) {
    _assert (getOpMode(o) == iABC);
    _assert (getBMode(o) != OpArgN || b == 0);
    _assert (getCMode(o) != OpArgN || c == 0);
    return this.code(CREATE_ABC(o, a, b, c), this.ls.lastline);
  }


  int codeABx(int o, int a, int bc) {
    _assert (getOpMode(o) == iABx || getOpMode(o) == iAsBx);
    _assert (getCMode(o) == OpArgN);
    return this.code(CREATE_ABx(o, a, bc), this.ls.lastline);
  }


  void setlist(int base, int nelems, int tostore) {
    int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1;
    int b = (tostore == LUA_MULTRET) ? 0 : tostore;
    _assert (tostore != 0);
    if (c <= MAXARG_C)
      this.codeABC(OP_SETLIST, base, b, c);
    else {
      this.codeABC(OP_SETLIST, base, b, 0);
      this.code(c, this.ls.lastline);
    }
    this.freereg = base + 1; /* free registers with list values */
  }
   
}
TOP

Related Classes of org.luaj.vm2.compiler.FuncState

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.