/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 
 * $Id: OPunary.java,v 1.6 2000/11/12 14:59:34 metlov Exp $
 *
 * This file is part of the Java Expressions Library (JEL).
 *   For more information about JEL visit :
 *    http://galaxy.fzu.cz/JEL/
 *
 * (c) 1998 -- 2000 by Konstantin Metlov(metlov@fzu.cz);
 *
 * JEL is Distributed under the terms of GNU General Public License.
 *    This code comes with ABSOLUTELY NO WARRANTY.
 *  For license details see COPYING file in this directory.
 */

package gnu.jel;

import gnu.jel.debug.Debug;
import gnu.jel.reflect.Method;

public class OPunary extends OPfunction {
  protected int code; // operation code

  protected int uwrpCode=0; // what to code to unwrap the object
  protected int implCode=0;
  protected int uwrpsTo=-1;

  // The full list of codes is :
  // 0  -- negation (applicable to anything except boolean)
  // 1  -- bitwise not (applicable to all integral types)
  // 2  -- logical not (applicable to booleans only)
  // 3  -- return
  // 4  -- convert to boolean
  // 5  -- convert to byte
  // 6  -- convert to char
  // 7  -- convert to short
  // 8  -- convert to int
  // 9  -- convert to long
  // 10  -- convert to float
  // 11 -- convert to double
  // 12 -- convert to object (in this case the cls parameter gives class)
  // 13 -- convert to void (throw from stack)
  // 14 -- convert string to temporary string buffer (TSB)
  // 15 -- convert temporary string buffer (TSB) to string

  protected final static byte[] unary_prmtns;
  
  private final static int[][] una;

  private final static String[] opSymbols;
  
  private final static String[] opNames;

  static {
    unary_prmtns=(byte[])TableKeeper.getTable("unary_prmtns");
    una=(int[][])TableKeeper.getTable("una");
    opSymbols=(String[])TableKeeper.getTable("opSymbols");
    opNames=(String[])TableKeeper.getTable("opNames");

    if (Debug.enabled)
      Debug.assert((opNames.length==opSymbols.length) &&
                   (opNames.length==una.length));
  };

  /**
   * Constructs a new unary operation.
   * <P>Codes are following:
   * <PRE>
   * 0  -- negation (applicable to anything except boolean)
   * 1  -- bitwise not (applicable to all integral types)
   * 2  -- logical not (applicable to booleans only)
   * 3  -- return the type in stack
   * </PRE>
   * @param typesStk stack holding the current types (will be updated)
   * @param code operation code
   * @param cls class to convert to (used only if code==11)
   */
  public OPunary(TypesStack typesStk, int code) 
    throws CompilationException {
    if (Debug.enabled)
      Debug.assert((code>=0) && (code<=3));

    this.code=code;
    
    int opID=typesStk.peekID();
    int unwrpID=TypesStack.unwrapType[opID];
    Class opType=typesStk.pop();

    // unwrap object
    if (unwrpID!=opID) {
      uwrpCode=((opID-12+11)<<8)+0x00FE;
      uwrpsTo=unwrpID;
    };

    if ((implCode=una[code][unwrpID])==0xFF) {
      // operation is not defined on types
      Object[] paramsExc={opNames[code],opType};
      throw new CompilationException(-1,28,paramsExc);
    };

    // ordinary ops do unary promotion
    // return stores the type, destroys the value
    if (code!=3) {       
      resID=unary_prmtns[unwrpID];
      typesStk.pushID(resID,null);      
    } else resID=TypesStack.baseType[unwrpID]; 
  };

  /**
   * Creates conversion operation to the given class.
   * @param typesStk stack holding the current types (will be updated)
   * @param targetID ID of primitive type to convert to.
   * @param targetClass the class to convert to, in case cldID=8
   * @param allownarrowing if narrowing conversions are allowed.
   */
  public OPunary(TypesStack typesStk, int targetID, Class targetClass,
                 boolean allownarrowing) throws CompilationException {
    if (Debug.enabled)
      Debug.assert((targetID!=8) || (targetClass!=null));

    // set the result type
    resID=targetID;
    resType=(targetClass!=null?targetClass:TypesStack.specialTypes[targetID]);

    Class currClass=typesStk.peek();
    int   currID=typesStk.peekID();

    int   unwrappedCurrID=TypesStack.unwrapType[currID];

    // The next if is needed because base type correctly says TSBs and Strings
    // are references, but we need to treat them separately here.
    if ((resID==10) || (resID==11))
      code=4+resID;
    else {
      code=4+TypesStack.baseType[resID];
      if (unwrappedCurrID!=currID) {
        uwrpCode=((currID-12+11)<<8)+0x00FE;
        uwrpsTo=unwrappedCurrID;
      };
    };

    //    if (!((TypesStack.baseType[unwrappedCurrID]==8) &&
    //          resType.isAssignableFrom(currClass))) {
      // widening reference conversion does not require any code
      if ((implCode=una[code][unwrappedCurrID])==0xFF) {
        Debug.println("code="+code);
        // can't convert at all
        Object[] paramsExc={currClass,resType};
        throw new CompilationException(-1,21,paramsExc);
      };
      //    };
    
    if (!(allownarrowing || 
          TypesStack.isWidening(currID,currClass,resID,resType))) {
      // can't do narrowing conversions automatically
      Object[] paramsExc={currClass,resType};
      throw new CompilationException(-1,22,paramsExc);
    };
    
    typesStk.pop();
    typesStk.pushID(resID,resType);
  };

//    /**
//     * Creates conversion operation to the given class.
//     * @param typesStk stack holding the current types (will be updated)
//     * @param cls the class to convert to, in case cldID=8
//     * @param allownarrowing if narrowing conversions are allowed.
//     */
//    public OPunary(TypesStack typesStk, Class cls,
//                   boolean allownarrowing) throws CompilationException {
//      this(typesStk,TypesStack.typeID(cls),cls,allownarrowing);
//    };


  /**
   * Returns number of parameters for this function.
   */
  public int getNParams() {
    return 1;
  };

  protected void compile_pre(ClassFile cf) {
    if (code==2) cf.code(0xFB); // equivalent to cf.labels_block();
    if (code==14) {
      cf.code(0x00591CFDBBL); // STR => TSB
      //                      | new
      //                          <CP: java.lang.StringBuffer>
      //                      | dup
      cf.typesStk.pushID(10); // pushes ref to TSB
      cf.typesStk.pushID(10); // pushes ref to TSB
    };
  };

  protected void compile(ClassFile cf) {
    cf.code(uwrpCode); // unwrap object if needed
    
    if (uwrpsTo>=0) { // note the unwrapping on stack
      cf.typesStk.pop();
      cf.typesStk.pushID(uwrpsTo);
    };
            
    cf.code(implCode);

    if (code==12) // code CP index for conversion to reference
      cf.codeI(cf.getIndex(resType,9));
    
    // get rid of this switch is the task for the NEXT major update
    switch(code) {
    case 2:  // logical inversion does not change stack
      break;
    case 14: // conversion to TSB throws one extra word from stack
      cf.typesStk.pop();
    case 3:
    case 13:
      cf.typesStk.pop(); // return and (void) throw one word, add nothing
      break;
    case 1: // bitwise not may have excess item on stack
      cf.typesStk.tempExcessWords(TypesStack.stkoccup[resID]);
    default: // other ops throw one word replace it by another
      cf.typesStk.pop();
      cf.typesStk.pushID(resID,resType);        
    };

  };

  /**
   * Attempts to perform this operation.
   * @param list is the list of OPs this one belong to, 
   *             if eval is unsuccessful this list is not modified.
   */
  protected void eval(OPlist list) {
    if (! (prev instanceof OPload)) return; // operand is not ready

    if (code==3) return; // "return" operation can't be evaluated
    if (code==13) return; // "void" operation can't be evaluated
    if (code==12) return; // "reference cast" operation can't be evaluated

    OPload opl=(OPload) prev;

    if (code==2) { // logical not
      if (((Boolean)opl.what).booleanValue())
        opl.what=Boolean.FALSE;
      else
        opl.what=Boolean.TRUE;
    } else if (code<2) {
      Number val=TypesStack.widen(opl.what,opl.resID);
      switch(code) {
      case 0:  // negation
        if (opl.resID>5)
          val=new Double(-val.doubleValue());
        else
          val=new Long(-val.longValue());
        break;
      case 1:  // bitwise complement
        val=new Long(~val.longValue());
        break;
      default:
        if (Debug.enabled)
          Debug.assert(code>=0,"Wrong unary opcode.");
      };
      opl.what=TypesStack.narrow(val,resID);
      opl.resID=resID;
    } else {
      // conversion operations
      if (code==14) { // STR->TSB
        opl.what=new StringBuffer((String) opl.what);
      } else if (code==15) { // TSB->STR
        opl.what=opl.what.toString();
      } else {
        opl.what=TypesStack.narrow(TypesStack.widen(opl.what,opl.resID),
                                   resID);
      };
      opl.resID=resID;
    };
    list.remove(this);
  };

  public String toString() {
    return opSymbols[code];
  };
  
};

