/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 
 * $Id: OPcall.java,v 1.4 2000/09/09 19:51:48 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.reflect.Member;
import gnu.jel.debug.Debug;
import java.util.Stack;


public class OPcall extends OPfunction {
  private Member m; // member to eval (null for the local variable access)
  // local variable number (in case m=null), number of formal parameters of
  // the method to call otherwise.
  private int nplv=0;
  private boolean attemptEval=true;

  /**
   * Prepares a new method/field call/get operation to be added to the code.
   * @param typesStk stack holding the current types (will be updated)
   * @param paramOPs stack holding the references to OPs after which
   *                 the type conversion OP for corresponding parameter
   *                 can be inserted (will be updated).
   * @param m method/field to call/get.
   * @param list current list of operations
   * @param attemptEval indicates if the method call should be attempted
   *                    at the compile time
   */
  public OPcall(TypesStack typesStk, Stack paramOPs, Member m,
                OPlist list, boolean attemptEval) 
    throws CompilationException {
    this.m=m;
    this.attemptEval=attemptEval;
    Class[] reqParamTypes=m.getParameterTypes();
    nplv=reqParamTypes.length;
    
    int thisIdx=0;
    if ((m.getModifiers() & 0x0008) == 0) { // method is not static
      thisIdx=-1;
      nplv++;
    };

    // convert formal and actual paramter types and "this", if needed    
    for(int i=reqParamTypes.length-1;i>=thisIdx;i--) {
      Class cReq=(i>=0?reqParamTypes[i]:m.getDeclaringClass());
      
      // add convert type OP
      if ((typesStk.peekID()==10) || (typesStk.peek()!=cReq))
        list.addAfter((OP)paramOPs.peek(),
                      new OPunary(typesStk,TypesStack.typeID(cReq),cReq,i<0));
      
      // throw out
      paramOPs.pop();
      typesStk.pop(); 
    };
    
    // push & store the result type
    typesStk.pushID(resID=m.getTypeID(),resType=m.getType());
  };

  /**
   * Prepares access to the local variable (formal parameter) of method.
   * @param typesStk stack holding the current types (will be updated)
   * @param paramOPs stack holding the references to OPs after which
   *                 the type conversion OP for corresponding parameter
   *                 can be inserted (will be updated).
   * @param lvar local variable number.
   * @param type local variable type.
   */
  public OPcall(TypesStack typesStk, int lvarn, Class type) {
    this.m=null;
    this.nplv=lvarn;
    typesStk.push(type);
    resID=typesStk.peekID();
    resType=type;
  };

  /**
   * Returns number of parameters for this function.
   */
  public int getNParams() {
    if (m==null) return 0;
    return nplv;
  };

  /**
   * Attempts to evaluate this function.
   * @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 (!attemptEval) return; // do not eval if not requested.
    if (m==null) return; // can't eval local variable loads
    if ((m.getModifiers() & 0x0008) ==0) return; // can't eval non-static
    if ((resID>7) && (resID!=11)) return; // can't hold object constants

    try {
      Object[] params=null;
      int nParams=nplv;
      if (nParams>0) {
        params=new Object[nParams];
        OPload cOPload= (OPload) prev;
        for(int i=nParams-1;i>=0;i--) {
          params[i]=cOPload.what;
          cOPload=(OPload) cOPload.prev;
        };
      };
      Object res=m.eval(null,params);
      
      try { // if there are problems below they should be reported
        if (nParams==0) { // if there are no parameters
          OPload opl=new OPload(null,resType,res);
          list.addBefore(this,opl);
          list.remove(this);
        } else {
          // remove this op and parameters - 1
          OP cop=this;
          for(int i=0;i<nParams;i++) {
            OP prv=cop.prev; list.remove(cop); cop=prv;
          };
          // patch cop, which is the last (first) OPload, to contain the result
          cop.resID=resID;
          ((OPload) cop).what=res;
        };

      } catch (Throwable thr) {
        if (Debug.enabled)
          Debug.reportThrowable(thr);
      };
      
    } catch (Throwable t) {
      // do nothing, just can't eval
    };
  };

  // compilation code

  protected void compile_pre(ClassFile cf) {
    if (m!=null) cf.labels_block();
  };

  protected void compile_par(ClassFile cf, int n) {
    cf.ensure_value();
    // if ((m!=null) && (reqParamTypes.length>0)) cf.labels_unblock();
  };


  //  int[][] load={
  //    //wide  shrt  0    1    2    3
  //    {0x15c4,0x15,0x1a,0x1b,0x1c,0x1d}, // Z
  //    {0x15c4,0x15,0x1a,0x1b,0x1c,0x1d}, // B
  //    {0x15c4,0x15,0x1a,0x1b,0x1c,0x1d}, // C
  //    {0x15c4,0x15,0x1a,0x1b,0x1c,0x1d}, // S
  //    {0x15c4,0x15,0x1a,0x1b,0x1c,0x1d}, // I
  //    {0x16c4,0x16,0x1e,0x1f,0x20,0x21}, // J
  //    {0x17c4,0x17,0x22,0x23,0x24,0x25}, // F
  //    {0x18c4,0x18,0x26,0x27,0x28,0x29}, // D
  //    {0x19c4,0x19,0x2a,0x2b,0x2c,0x2d}  // L
  //  };

  protected void compile(ClassFile cf) {
    if (m==null) {
      // load the local variable with a given number
      int lvt=resID-4;
      if (lvt<0) lvt=0;

      int lvarn_translated=cf.paramsVars[nplv];

      if (lvarn_translated<4) 
        cf.code(0x1a+lvt*4+lvarn_translated);
      else if (lvarn_translated<=255)
        cf.code(0x15+lvt+(lvarn_translated<<8));
      else {
        cf.code(((0x15+lvt)<<8)+0xc4);
        cf.codeI(lvarn_translated);
      };
    } else { 
      cf.labels_unblock();
      
      // call the method / get field
      m.code(cf);
      
      int nParams=nplv;
      while(nParams-->0) cf.typesStk.pop();
    };
    
    cf.typesStk.pushID(resID,resType);
  };

  public String toString() {
    if (m==null)
      return "{"+nplv+"}";
    else
      return m.getName();
  };
};
