001: /*****************************************************************************
002: * *
003: * This file is part of the BeanShell Java Scripting distribution. *
004: * Documentation and updates may be found at http://www.beanshell.org/ *
005: * *
006: * Sun Public License Notice: *
007: * *
008: * The contents of this file are subject to the Sun Public License Version *
009: * 1.0 (the "License"); you may not use this file except in compliance with *
010: * the License. A copy of the License is available at http://www.sun.com *
011: * *
012: * The Original Code is BeanShell. The Initial Developer of the Original *
013: * Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
014: * (C) 2000. All Rights Reserved. *
015: * *
016: * GNU Public License Notice: *
017: * *
018: * Alternatively, the contents of this file may be used under the terms of *
019: * the GNU Lesser General Public License (the "LGPL"), in which case the *
020: * provisions of LGPL are applicable instead of those above. If you wish to *
021: * allow use of your version of this file only under the terms of the LGPL *
022: * and not to allow others to use your version of this file under the SPL, *
023: * indicate your decision by deleting the provisions above and replace *
024: * them with the notice and other provisions required by the LGPL. If you *
025: * do not delete the provisions above, a recipient may use your version of *
026: * this file under either the SPL or the LGPL. *
027: * *
028: * Patrick Niemeyer (pat@pat.net) *
029: * Author of Learning Java, O'Reilly & Associates *
030: * http://www.pat.net/~pat/ *
031: * *
032: *****************************************************************************/package bsh;
033:
034: /**
035: Implement binary expressions...
036: Note: this is too complicated... need some cleanup and simplification.
037: @see Primitive.binaryOperation
038: */
039: class BSHBinaryExpression extends SimpleNode implements ParserConstants {
040: public int kind;
041:
042: BSHBinaryExpression(int id) {
043: super (id);
044: }
045:
046: public Object eval(CallStack callstack, Interpreter interpreter)
047: throws EvalError {
048: Object lhs = ((SimpleNode) jjtGetChild(0)).eval(callstack,
049: interpreter);
050:
051: /*
052: Doing instanceof? Next node is a type.
053: */
054: if (kind == INSTANCEOF) {
055: // null object ref is not instance of any type
056: if (lhs == Primitive.NULL)
057: return Primitive.FALSE;
058:
059: Class rhs = ((BSHType) jjtGetChild(1)).getType(callstack,
060: interpreter);
061: /*
062: // primitive (number or void) cannot be tested for instanceof
063: if (lhs instanceof Primitive)
064: throw new EvalError("Cannot be instance of primitive type." );
065: */
066: /*
067: Primitive (number or void) is not normally an instanceof
068: anything. But for internal use we'll test true for the
069: bsh.Primitive class.
070: i.e. (5 instanceof bsh.Primitive) will be true
071: */
072: if (lhs instanceof Primitive)
073: if (rhs == bsh.Primitive.class)
074: return Primitive.TRUE;
075: else
076: return Primitive.FALSE;
077:
078: // General case - performe the instanceof based on assignability
079: boolean ret = Types.isJavaBaseAssignable(rhs, lhs
080: .getClass());
081: return new Primitive(ret);
082: }
083:
084: // The following two boolean checks were tacked on.
085: // This could probably be smoothed out.
086:
087: /*
088: Look ahead and short circuit evaluation of the rhs if:
089: we're a boolean AND and the lhs is false.
090: */
091: if (kind == BOOL_AND || kind == BOOL_ANDX) {
092: Object obj = lhs;
093: if (isPrimitiveValue(lhs))
094: obj = ((Primitive) lhs).getValue();
095: if (obj instanceof Boolean
096: && (((Boolean) obj).booleanValue() == false))
097: return Primitive.FALSE;
098: }
099: /*
100: Look ahead and short circuit evaluation of the rhs if:
101: we're a boolean AND and the lhs is false.
102: */
103: if (kind == BOOL_OR || kind == BOOL_ORX) {
104: Object obj = lhs;
105: if (isPrimitiveValue(lhs))
106: obj = ((Primitive) lhs).getValue();
107: if (obj instanceof Boolean
108: && (((Boolean) obj).booleanValue() == true))
109: return Primitive.TRUE;
110: }
111:
112: // end stuff that was tacked on for boolean short-circuiting.
113:
114: /*
115: Are both the lhs and rhs either wrappers or primitive values?
116: do binary op
117: */
118: boolean isLhsWrapper = isWrapper(lhs);
119: Object rhs = ((SimpleNode) jjtGetChild(1)).eval(callstack,
120: interpreter);
121: boolean isRhsWrapper = isWrapper(rhs);
122: if ((isLhsWrapper || isPrimitiveValue(lhs))
123: && (isRhsWrapper || isPrimitiveValue(rhs))) {
124: // Special case for EQ on two wrapper objects
125: if ((isLhsWrapper && isRhsWrapper && kind == EQ)) {
126: /*
127: Don't auto-unwrap wrappers (preserve identity semantics)
128: FALL THROUGH TO OBJECT OPERATIONS BELOW.
129: */
130: } else
131: try {
132: return Primitive.binaryOperation(lhs, rhs, kind);
133: } catch (UtilEvalError e) {
134: throw e.toEvalError(this , callstack);
135: }
136: }
137: /*
138: Doing the following makes it hard to use untyped vars...
139: e.g. if ( arg == null ) ...what if arg is a primitive?
140: The answer is that we should test only if the var is typed...?
141: need to get that info here...
142:
143: else
144: {
145: // Do we have a mixture of primitive values and non-primitives ?
146: // (primitiveValue = not null, not void)
147:
148: int primCount = 0;
149: if ( isPrimitiveValue( lhs ) )
150: ++primCount;
151: if ( isPrimitiveValue( rhs ) )
152: ++primCount;
153:
154: if ( primCount > 1 )
155: // both primitive types, should have been handled above
156: throw new InterpreterError("should not be here");
157: else
158: if ( primCount == 1 )
159: // mixture of one and the other
160: throw new EvalError("Operator: '" + tokenImage[kind]
161: +"' inappropriate for object / primitive combination.",
162: this, callstack );
163:
164: // else fall through to handle both non-primitive types
165:
166: // end check for primitive and non-primitive mix
167: }
168: */
169:
170: /*
171: Treat lhs and rhs as arbitrary objects and do the operation.
172: (including NULL and VOID represented by their Primitive types)
173: */
174: //System.out.println("binary op arbitrary obj: {"+lhs+"}, {"+rhs+"}");
175: switch (kind) {
176: case EQ:
177: return (lhs == rhs) ? Primitive.TRUE : Primitive.FALSE;
178:
179: case NE:
180: return (lhs != rhs) ? Primitive.TRUE : Primitive.FALSE;
181:
182: case PLUS:
183: if (lhs instanceof String || rhs instanceof String)
184: return lhs.toString() + rhs.toString();
185:
186: // FALL THROUGH TO DEFAULT CASE!!!
187:
188: default:
189: if (lhs instanceof Primitive || rhs instanceof Primitive)
190: if (lhs == Primitive.VOID || rhs == Primitive.VOID)
191: throw new EvalError(
192: "illegal use of undefined variable, class, or 'void' literal",
193: this , callstack);
194: else if (lhs == Primitive.NULL || rhs == Primitive.NULL)
195: throw new EvalError(
196: "illegal use of null value or 'null' literal",
197: this , callstack);
198:
199: throw new EvalError("Operator: '" + tokenImage[kind]
200: + "' inappropriate for objects", this , callstack);
201: }
202: }
203:
204: /*
205: object is a non-null and non-void Primitive type
206: */
207: private boolean isPrimitiveValue(Object obj) {
208: return ((obj instanceof Primitive) && (obj != Primitive.VOID) && (obj != Primitive.NULL));
209: }
210:
211: /*
212: object is a java.lang wrapper for boolean, char, or number type
213: */
214: private boolean isWrapper(Object obj) {
215: return (obj instanceof Boolean || obj instanceof Character || obj instanceof Number);
216: }
217: }
|