/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* $Id: OPbinary.java 490 2006-10-01 16:08:04Z metlov $
*
* This file is part of the Java Expressions Library (JEL).
* For more information about JEL visit :
* http://kinetic.ac.donetsk.ua/JEL/
*
* (c) 1998 -- 2007 by Konstantin Metlov(metlov@kinetic.ac.donetsk.ua);
*
* 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 java.util.Stack;
/**
* A tree node, representing binary operation.
*/
public class OPbinary extends OP {
/** code of this operation */
public int code;
/** index into ops array to get code for this op. */
private int opsIDX;
// Names of binary operations by ID in the readable form.
private final static String[] opNames;
/** binary promotions of base types */
protected final static byte[][] promotions;
// code chunks to implement the ops
private final static int[][] ops;
// type of operands promotion by the opcode
private static final byte[] promotionTypes;
static {
promotions=(byte[][])TableKeeper.getTable("promotions");
ops=(int[][])TableKeeper.getTable("ops");
promotionTypes=(byte[])TableKeeper.getTable("promotionTypes");
opNames=(String[])TableKeeper.getTable("binOpNames");
};
/**
* Constructs a new binary operation.
* <P>Codes are following:
* <PRE>
* 0 -- addition
* 1 -- substraction
* 2 -- multiplication
* 3 -- division
* 4 -- remainder
* 5 -- bitwise AND
* 6 -- bitwise OR
* 7 -- bitwise and logical XOR
* 8 -- comparizon for equality
* 9 -- comparizon for non-equality
* 10 -- comparizon for "less" <
* 11 -- comparizon for "greater or equal" >=
* 12 -- comparizon for "greater" >
* 13 -- comparizon for "less or equal" <=
* 14 -- bitwise left shift <<
* 15 -- bitwise right signed shift >>
* 16 -- bitwise right unsigned shift >>>
* 17 -- logical conjunction operator (AND)
* 18 -- logical disjunction operator (OR)
* 19 -- array element access operation
* 20 -- reserved (used internally for string concatenation)
* </PRE>
* @param paramOPs stack holding the operands
* @param opcode is the operation code
*/
public OPbinary(Stack<OP> paramOPs, int opcode) throws CompilationException {
if (Debug.enabled)
Debug.check((opcode>=0) && (opcode<opNames.length));
// get the operands types
chi=new OP[2];
chi[1]=(OP)paramOPs.pop();
chi[0]=(OP)paramOPs.pop();
int op2ID=chi[1].resID;
Class op2Type=chi[1].resType;
int op1ID=chi[0].resID;
Class op1Type=chi[0].resType;
// separate out string concatenation
if (((op1ID==10) || (unwrapType[op1ID]==11)) && (opcode==0))
opcode=20;
this.code=opcode;
// Debug.println("opcode="+opcode+" op1ID="+op1ID+" op2ID="+op2ID);
// perform the promotion of operands and determine the index
// variables opsIDX, resID, optionally resType (if resID=8) and
// the following will be defined
int op2cvtID=op2ID,op1cvtID=op1ID;
resID=-1;
boolean second_narrowing=false;
int op1IDuwrp=unwrapType[op1ID];
int op2IDuwrp=unwrapType[op2ID];
switch (promotionTypes[opcode]) {
case 0: // binary promotion with boolean result
resID=0;
case 1: // binary promotion
op1cvtID=op2cvtID=opsIDX=
promotions[op1IDuwrp][op2IDuwrp];
if (resID<0) resID=opsIDX;
if (opsIDX==-1) { // types are incompatible (can't promote)
Object[] paramsExc={op1Type,op2Type,opNames[opcode]};
throw new CompilationException(15,paramsExc);
};
break;
case 3: // array promotion
resType=(op1Type!=null?op1Type.getComponentType():null);
if (resType==null)
throw new CompilationException(18,null);
resID=typeID(resType);
opsIDX=(resID>=8?8:resID);
case 2: // unary promotion of the first operand, second to int
if (resID<0) {
resID=op1cvtID=opsIDX=
OPunary.unary_prmtns[op1IDuwrp];
second_narrowing=true;
};
// integral types mask
// 7 6 5 4 3 2 1 0
// 0 0 1 1 1 1 1 0 = 0x3E
if (((0x3E>>op2IDuwrp) & 1)==0) { // type is not integral
Object[] paramsExc={opNames[opcode],op2Type};
throw new CompilationException(27,paramsExc);
};
op2cvtID=4;
break;
case 4: // string concatenation promotion
opsIDX=(op2ID>11?8:op2ID);
op1cvtID=10; // TSB
resID=10; // TSB
break;
default:
if (Debug.enabled)
Debug.println("Wrong promotion type for binary OP "+
promotionTypes[opcode]);
};
// check if the OP can be implemented
if (ops[opcode][opsIDX]==0xFF) { // operation is not defined on types
Object[] paramsExc={opNames[opcode],op1Type,op2Type};
throw new CompilationException(16,paramsExc);
};
// insert type conversion opcode
if ((op1ID!=op1cvtID) && (op1cvtID!=8)) {
paramOPs.push(chi[0]);
chi[0]=new OPunary(paramOPs,op1cvtID,null,op1cvtID==10);
// can narrow to TSB
};
if ((op2ID!=op2cvtID) && (op2cvtID!=8)) {
paramOPs.push(chi[1]);
chi[1]=new OPunary(paramOPs,op2cvtID,null,second_narrowing);
};
if (resID!=8)
resType=specialTypes[resID];
};
public void compile(ClassFile cf) {
if ((code==17) || (code==18)) {
chi[0].compile(cf);
cf.code(code-(17 - 0xF4)); // logical_param AND/OR
chi[1].compile(cf);
cf.code(code-(17 - 0xF6)); // logical_end AND/OR
} else {
chi[0].compile(cf);
cf.code(0xFA); // ensure value;
chi[1].compile(cf);
cf.code(0xFA); // ensure value;
cf.code(ops[code][opsIDX] & 0xFFFFFFFFL);
// & is needed to prevent sign extension when converting "int" ops
// element into "long" argument of code.
cf.noteStk(chi[0].resID,-1);
cf.noteStk(chi[1].resID,-1);
if (cf.currJump==0) // jumps do not load anything to the stack
cf.noteStk(-1,resID);
};
};
public Object eval() throws Exception {
Object c1w=null;
boolean ep1=false;
int c1ID=chi[0].resID;
Object c2w=null;
boolean ep2=false;
int c2ID=chi[1].resID;
try {
c1w=chi[0].eval();
} catch (Exception e) {
ep1=true;
};
try {
c2w=chi[1].eval();
} catch (Exception e) {
ep2=true;
};
try {
if (ep1 || ep2 || (code==19)) // array access can't be evaluated.
throw new Exception();
if (code==20) {
((StringBuffer)c1w).append(String.valueOf(c2w));
} else if ((c1ID>=8) || (c2ID>=8)) {
// the only way the objects appear in this context
// are the comparisons
if (Debug.enabled) {
Debug.check((code>=8)||(code<=13),
"only comparisons and concatenation binops can "+
"operate on objects.");
Debug.check(c1ID!=10, "No TSB in this context 1");
Debug.check(c1ID!=10, "No TSB in this context 2");
};
// only string literal comparisons are interpreted
if ((c1ID!=11) || (c2ID!=11))
throw new Exception();
// compare the strings
int res=CompiledExpression.compare((String)c1w,(String)c2w);
c1w=((0x3172A>>>(3*(code-8)))&((res>0)?1:((res<0)?4:2)))>0
?Boolean.TRUE:Boolean.FALSE;
} else { // binary on primitive types
// Widen
Number n1=widen(c1w,c1ID);
Number n2=widen(c2w,c2ID);
// Perform
boolean boolres=false;
boolean resbool=false;
if ((opsIDX>=6) && (opsIDX<=7)) { // operations on floating point
double d1=n1.doubleValue(),d2=n2.doubleValue();
boolean wrop=false;
switch (code) {
case 0 : d1=d1+d2; break; //PL
case 1 : d1=d1-d2; break; //MI
case 2 : d1=d1*d2; break; //MU
case 3 : d1=d1/d2; break; //DI
case 4 : d1=d1%d2; break; //RE
case 5 : wrop=true;break; //AN
case 6 : wrop=true;break; //OR
case 7 : wrop=true;break; //XO
case 8 : boolres=true; resbool=(d1==d2); break; //EQ
case 9 : boolres=true; resbool=(d1!=d2); break; //NE
case 10: boolres=true; resbool=(d1<d2); break; //LT
case 11: boolres=true; resbool=(d1>=d2); break; //GE
case 12: boolres=true; resbool=(d1>d2); break; //GT
case 13: boolres=true; resbool=(d1<=d2); break; //LE
default :
wrop=true;
};
if (Debug.enabled && wrop)
Debug.println("Wrong operation on float ("+code+").");
if (!boolres) n1=new Double(d1);
else { // booleans are represented by longs temporarily
if (resbool) n1=new Long(1L); else n1=new Long(0);
};
} else { // operations on integers
long l1=n1.longValue(),l2=n2.longValue();
switch (code) {
case 0: l1=l1+l2;break; //PL
case 1: l1=l1-l2;break; //MI
case 2: l1=l1*l2; break; //MU
case 3: l1=l1/l2; break; //DI
case 4: l1=l1%l2; break; //RE
case 17:
case 5: l1=l1&l2; break; //AN
case 18:
case 6: l1=l1|l2; break; //OR
case 7: l1=l1^l2; break; //XO
case 8 : boolres=true; l1=(l1==l2?1L:0L); break; //EQ
case 9 : boolres=true; l1=(l1!=l2?1L:0L); break; //NE
case 10: boolres=true; l1=(l1< l2?1L:0L); break; //LT
case 11: boolres=true; l1=(l1>=l2?1L:0L); break; //GE
case 12: boolres=true; l1=(l1> l2?1L:0L); break; //GT
case 13: boolres=true; l1=(l1<=l2?1L:0L); break; //LE
case 14: l1=l1<<l2; break; // LS
case 15: l1=l1>>l2; break; // RS
case 16: { // for this kind of shifts the bit width of variable is
// important
if (resID==4) //=because there is unary numeric promotion before op
l1=(int)l1>>>l2;
else
l1=l1>>>l2;
break;
} // RUS
default :
if (Debug.enabled)
Debug.println("Wrong operation on integer ("+code+").");
};
n1=new Long(l1);
};
// Narrow
if (boolres) {
c1w=narrow(n1,0);
} else c1w=narrow(n1,resID);
};
return c1w;
} catch (Exception thr) {
// if can't evaluate -- replace operands at least
if (!ep1)
chi[0]=new OPload(chi[0],c1w);
if (!ep2)
chi[1]=new OPload(chi[1],c2w);
throw thr;
// Debug.reportThrowable(thr);
// IGNORE
}
};
};