Use Tree Navigation
public class

Expression

extends Node
/*
 * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package sun.tools.tree;

import sun.tools.java.*;
import sun.tools.asm.Label;
import sun.tools.asm.Assembler;
import java.io.PrintStream;
import java.util.Hashtable;

/**
 * WARNING: The contents of this source file are not part of any
 * supported API.  Code that depends on them does so at its own risk:
 * they are subject to change or removal without notice.
 */

public
class Expression extends Node {
   
Type type;

   
/**
     * Constructor
     */

   
Expression(int op, long where, Type type) {
       
super(op, where);
       
this.type = type;
   
}

   
/**
     * Type checking may assign a more complex implementation
     * to an innocuous-looking expression (like an identifier).
     * Return that implementation, or the original expression itself
     * if there is no special implementation.
     * <p>
     * This appears at present to be dead code, and is not called
     * from within javac.  Access to the implementation generally
     * occurs within the same class, and thus uses the underlying
     * field directly.
     */

   
public Expression getImplementation() {
       
return this;
   
}

   
public Type getType() {
       
return type;
   
}

   
/**
     * Return the precedence of the operator
     */

   
int precedence() {
       
return (op < opPrecedence.length) ? opPrecedence[op] : 100;
   
}

   
/**
     * Order the expression based on precedence
     */

   
public Expression order() {
       
return this;
   
}

   
/**
     * Return true if constant, according to JLS 15.27.
     * A constant expression must inline away to a literal constant.
     */

   
public boolean isConstant() {
       
return false;
   
}

   
/**
     * Return the constant value.
     */

   
public Object getValue() {
       
return null;
   
}

   
/**
     * Check if the expression is known to be equal to a given value.
     * Returns false for any expression other than a literal constant,
     * thus should be called only after simplification (inlining) has
     * been performed.
     */

   
public boolean equals(int i) {
       
return false;
   
}
   
public boolean equals(boolean b) {
       
return false;
   
}
   
public boolean equals(Identifier id) {
       
return false;
   
}
   
public boolean equals(String s) {
       
return false;
   
}

   
/**
     * Check if the expression must be a null reference.
     */

   
public boolean isNull() {
       
return false;
   
}

   
/**
     * Check if the expression cannot be a null reference.
     */

   
public boolean isNonNull() {
       
return false;
   
}

   
/**
     * Check if the expression is equal to its default static value
     */

   
public boolean equalsDefault() {
       
return false;
   
}


   
/**
     * Convert an expresion to a type
     */

   
Type toType(Environment env, Context ctx) {
        env
.error(where, "invalid.type.expr");
       
return Type.tError;
   
}

   
/**
     * Convert an expresion to a type in a context where a qualified
     * type name is expected, e.g., in the prefix of a qualified type
     * name.
     */

   
/*-----------------------------------------------------*
    Type toQualifiedType(Environment env, Context ctx) {
        env.error(where, "invalid.type.expr");
        return Type.tError;
    }
    *-----------------------------------------------------*/


   
/**
     * See if this expression fits in the given type.
     * This is useful because some larger numbers fit into
     * smaller types.
     * <p>
     * If it is an "int" constant expression, inline it, if necessary,
     * to examine its numerical value.  See JLS 5.2 and 15.24.
     */

   
public boolean fitsType(Environment env, Context ctx, Type t) {
       
try {
           
if (env.isMoreSpecific(this.type, t)) {
               
return true;
           
}
           
if (this.type.isType(TC_INT) && this.isConstant() && ctx != null) {
               
// Tentative inlining is harmless for constant expressions.
               
Expression n = this.inlineValue(env, ctx);
               
if (n != this && n instanceof ConstantExpression) {
                   
return n.fitsType(env, ctx, t);
               
}
           
}
           
return false;
       
} catch (ClassNotFound e) {
           
return false;
       
}
   
}

   
/** @deprecated (for backward compatibility) */
   
@Deprecated
   
public boolean fitsType(Environment env, Type t) {
       
return fitsType(env, (Context) null, t);
   
}

   
/**
     * Check an expression
     */

   
public Vset checkValue(Environment env, Context ctx, Vset vset, Hashtable exp) {
       
return vset;
   
}
   
public Vset checkInitializer(Environment env, Context ctx, Vset vset, Type t, Hashtable exp) {
       
return checkValue(env, ctx, vset, exp);
   
}
   
public Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) {
       
throw new CompilerError("check failed");
   
}

   
public Vset checkLHS(Environment env, Context ctx,
                           
Vset vset, Hashtable exp) {
        env
.error(where, "invalid.lhs.assignment");
        type
= Type.tError;
       
return vset;
   
}

   
/**
     * Return a <code>FieldUpdater</code> object to be used in updating the
     * value of the location denoted by <code>this</code>, which must be an
     * expression suitable for the left-hand side of an assignment.
     * This is used for implementing assignments to private fields for which
     * an access method is required.  Returns null if no access method is
     * needed, in which case the assignment is handled in the usual way, by
     * direct access.  Only simple assignment expressions are handled here
     * Assignment operators and pre/post increment/decrement operators are
     * are handled by 'getUpdater' below.
     * <p>
     * Called during the checking phase.
     */


   
public FieldUpdater getAssigner(Environment env, Context ctx) {
       
throw new CompilerError("getAssigner lhs");
   
}

   
/**
     * Return a <code>FieldUpdater</code> object to be used in updating the value of the
     * location denoted by <code>this</code>, which must be an expression suitable for the
     * left-hand side of an assignment.  This is used for implementing the assignment
     * operators and the increment/decrement operators on private fields that require an
     * access method, e.g., uplevel from an inner class.  Returns null if no access method
     * is needed.
     * <p>
     * Called during the checking phase.
     */


   
public FieldUpdater getUpdater(Environment env, Context ctx) {
       
throw new CompilerError("getUpdater lhs");
   
}

   
public Vset checkAssignOp(Environment env, Context ctx,
                             
Vset vset, Hashtable exp, Expression outside) {
       
if (outside instanceof IncDecExpression)
            env
.error(where, "invalid.arg", opNames[outside.op]);
       
else
            env
.error(where, "invalid.lhs.assignment");
        type
= Type.tError;
       
return vset;
   
}

   
/**
     * Check something that might be an AmbiguousName (refman 6.5.2).
     * A string of dot-separated identifiers might be, in order of preference:
     * <nl>
     * <li> a variable name followed by fields or types
     * <li> a type name followed by fields or types
     * <li> a package name followed a type and then fields or types
     * </nl>
     * If a type name is found, it rewrites itself as a <tt>TypeExpression</tt>.
     * If a node decides it can only be a package prefix, it sets its
     * type to <tt>Type.tPackage</tt>.  The caller must detect this
     * and act appropriately to verify the full package name.
     * @arg loc the expression containing the ambiguous expression
     */

   
public Vset checkAmbigName(Environment env, Context ctx, Vset vset, Hashtable exp,
                               
UnaryExpression loc) {
       
return checkValue(env, ctx, vset, exp);
   
}

   
/**
     * Check a condition.  Return a ConditionVars(), which indicates when
     * which variables are set if the condition is true, and which are set if
     * the condition is false.
     */

   
public ConditionVars checkCondition(Environment env, Context ctx,
                                       
Vset vset, Hashtable exp) {
       
ConditionVars cvars = new ConditionVars();
        checkCondition
(env, ctx, vset, exp, cvars);
       
return cvars;
   
}

   
/*
     * Check a condition.
     *
     * cvars is modified so that
     *    cvar.vsTrue indicates variables with a known value if result = true
     *    cvars.vsFalse indicates variables with a known value if !result
     *
     * The default action is to simply call checkValue on the expression, and
     * to see both vsTrue and vsFalse to the result.
     */


   
public void checkCondition(Environment env, Context ctx,
                               
Vset vset, Hashtable exp, ConditionVars cvars) {
        cvars
.vsTrue = cvars.vsFalse = checkValue(env, ctx, vset, exp);
       
// unshare side effects:
        cvars
.vsFalse = cvars.vsFalse.copy();
   
}

   
/**
     * Evaluate.
     *
     * Attempt to compute the value of an expression node.  If all operands are
     * literal constants of the same kind (e.g., IntegerExpression nodes), a
     * new constant node of the proper type is returned representing the value
     * as computed at compile-time.  Otherwise, the original node 'this' is
     * returned.
     */

   
Expression eval() {
       
return this;
   
}

   
/**
     * Simplify.
     *
     * Attempt to simplify an expression node by returning a semantically-
     * equivalent expression that is presumably less costly to execute.  There
     * is some overlap with the intent of 'eval', as compile-time evaluation of
     * conditional expressions and the short-circuit boolean operators is
     * performed here.  Other simplifications include logical identities
     * involving logical negation and comparisons.  If no simplification is
     * possible, the original node 'this' is returned.  It is assumed that the
     * children of the node have previously been recursively simplified and
     * evaluated.  A result of 'null' indicates that the expression may be
     * elided entirely.
     */

   
Expression simplify() {
       
return this;
   
}

   
/**
     * Inline.
     *
     * Recursively simplify each child of an expression node, destructively
     * replacing the child with the simplified result.  Also attempts to
     * simplify the current node 'this', and returns the simplified result.
     *
     * The name 'inline' is somthing of a misnomer, as these methods are
     * responsible for compile-time expression simplification in general.
     * The 'eval' and 'simplify' methods apply to a single expression node
     * only -- it is 'inline' and 'inlineValue' that drive the simplification
     * of entire expressions.
     */

   
public Expression inline(Environment env, Context ctx) {
       
return null;
   
}
   
public Expression inlineValue(Environment env, Context ctx) {
       
return this;
   
}

   
/**
     * Attempt to evaluate this expression.  If this expression
     * yields a value, append it to the StringBuffer `buffer'.
     * If this expression cannot be evaluated at this time (for
     * example if it contains a division by zero, a non-constant
     * subexpression, or a subexpression which "refuses" to evaluate)
     * then return `null' to indicate failure.
     *
     * It is anticipated that this method will be called to evaluate
     * concatenations of compile-time constant strings.  The call
     * originates from AddExpression#inlineValue().
     *
     * See AddExpression#inlineValueSB() for detailed comments.
     */

   
protected StringBuffer inlineValueSB(Environment env,
                                         
Context ctx,
                                         
StringBuffer buffer) {
       
Expression inlined = inlineValue(env, ctx);
       
Object val = inlined.getValue();

       
if (val == null && !inlined.isNull()){
           
// This (supposedly constant) expression refuses to yield
           
// a value.  This can happen, in particular, when we are
           
// trying to evaluate a division by zero.  It can also
           
// happen in cases where isConstant() is able to classify
           
// expressions as constant that the compiler's inlining
           
// mechanisms aren't able to evaluate; this is rare,
           
// and all such cases that we have found so far
           
// (e.g. 4082814, 4106244) have been plugged up.
           
//
           
// We return a null to indicate that we have failed to
           
// evaluate the concatenation.
           
return null;
       
}

       
// For boolean and character expressions, getValue() returns
       
// an Integer.  We need to take care, when appending the result
       
// of getValue(), that we preserve the type.
       
// Fix for 4103959, 4102672.
       
if (type == Type.tChar) {
            buffer
.append((char)((Integer)val).intValue());
       
} else if (type == Type.tBoolean) {
            buffer
.append(((Integer)val).intValue() != 0);
       
} else {
            buffer
.append(val);
       
}

       
return buffer;
   
}

   
public Expression inlineLHS(Environment env, Context ctx) {
       
return null;
   
}

   
/**
     * The cost of inlining this expression.
     * This cost controls the inlining of methods, and does not determine
     * the compile-time simplifications performed by 'inline' and friends.
     */

   
public int costInline(int thresh, Environment env, Context ctx) {
       
return 1;
   
}

   
/**
     * Code
     */

   
void codeBranch(Environment env, Context ctx, Assembler asm, Label lbl, boolean whenTrue) {
       
if (type.isType(TC_BOOLEAN)) {
            codeValue
(env, ctx, asm);
           
asm.add(where, whenTrue ? opc_ifne : opc_ifeq, lbl, whenTrue);
       
} else {
           
throw new CompilerError("codeBranch " + opNames[op]);
       
}
   
}
   
public void codeValue(Environment env, Context ctx, Assembler asm) {
       
if (type.isType(TC_BOOLEAN)) {
           
Label l1 = new Label();
           
Label l2 = new Label();

            codeBranch
(env, ctx, asm, l1, true);
           
asm.add(true, where, opc_ldc, new Integer(0));
           
asm.add(true, where, opc_goto, l2);
           
asm.add(l1);
           
asm.add(true, where, opc_ldc, new Integer(1));
           
asm.add(l2);
       
} else {
           
throw new CompilerError("codeValue");
       
}
   
}
   
public void code(Environment env, Context ctx, Assembler asm) {
        codeValue
(env, ctx, asm);

       
switch (type.getTypeCode()) {
         
case TC_VOID:
           
break;

         
case TC_DOUBLE:
         
case TC_LONG:
           
asm.add(where, opc_pop2);
           
break;

         
default:
           
asm.add(where, opc_pop);
           
break;
       
}
   
}
   
int codeLValue(Environment env, Context ctx, Assembler asm) {
       
print(System.out);
       
throw new CompilerError("invalid lhs");
   
}
   
void codeLoad(Environment env, Context ctx, Assembler asm) {
       
print(System.out);
       
throw new CompilerError("invalid load");
   
}
   
void codeStore(Environment env, Context ctx, Assembler asm) {
       
print(System.out);
       
throw new CompilerError("invalid store");
   
}

   
/**
     * Convert this expression to a string.
     */

   
void ensureString(Environment env, Context ctx, Assembler asm)
           
throws ClassNotFound, AmbiguousMember
   
{
       
if (type == Type.tString && isNonNull()) {
           
return;
       
}
       
// Make sure it's a non-null string.
       
ClassDefinition sourceClass = ctx.field.getClassDefinition();
       
ClassDeclaration stClass = env.getClassDeclaration(Type.tString);
       
ClassDefinition stClsDef = stClass.getClassDefinition(env);
       
// FIX FOR 4071548
       
// We use 'String.valueOf' to do the conversion, in order to
       
// correctly handle null references and efficiently handle
       
// primitive types.  For reference types, we force the argument
       
// to be interpreted as of 'Object' type, thus avoiding the
       
// the special-case overloading of 'valueOf' for character arrays.
       
// This special treatment would conflict with JLS 15.17.1.1.
       
if (type.inMask(TM_REFERENCE)) {
           
// Reference type
           
if (type != Type.tString) {
               
// Convert non-string object to string.  If object is
               
// a string, we don't need to convert it, except in the
               
// case that it is null, which is handled below.
               
Type argType1[] = {Type.tObject};
               
MemberDefinition f1 =
                    stClsDef
.matchMethod(env, sourceClass, idValueOf, argType1);
               
asm.add(where, opc_invokestatic, f1);
           
}
           
// FIX FOR 4030173
           
// If the argument was null, then value is "null", but if the
           
// argument was not null, 'toString' was called and could have
           
// returned null.  We call 'valueOf' again to make sure that
           
// the result is a non-null string.  See JLS 15.17.1.1.  The
           
// approach taken here minimizes code size -- open code would
           
// be faster.  The 'toString' method for an array class cannot
           
// be overridden, thus we know that it will never return null.
           
if (!type.inMask(TM_ARRAY|TM_NULL)) {
               
Type argType2[] = {Type.tString};
               
MemberDefinition f2 =
                    stClsDef
.matchMethod(env, sourceClass, idValueOf, argType2);
               
asm.add(where, opc_invokestatic, f2);
           
}
       
} else {
           
// Primitive type
           
Type argType[] = {type};
           
MemberDefinition f =
                stClsDef
.matchMethod(env, sourceClass, idValueOf, argType);
           
asm.add(where, opc_invokestatic, f);
       
}
   
}

   
/**
     * Convert this expression to a string and append it to the string
     * buffer on the top of the stack.
     * If the needBuffer argument is true, the string buffer needs to be
     * created, initialized, and pushed on the stack, first.
     */

   
void codeAppend(Environment env, Context ctx, Assembler asm,
                   
ClassDeclaration sbClass, boolean needBuffer)
           
throws ClassNotFound, AmbiguousMember
   
{
       
ClassDefinition sourceClass = ctx.field.getClassDefinition();
       
ClassDefinition sbClsDef = sbClass.getClassDefinition(env);
       
MemberDefinition f;
       
if (needBuffer) {
           
// need to create the string buffer
           
asm.add(where, opc_new, sbClass); // create the class
           
asm.add(where, opc_dup);
           
if (equals("")) {
               
// make an empty string buffer
                f
= sbClsDef.matchMethod(env, sourceClass, idInit);
           
} else {
               
// optimize by initializing the buffer with the string
                codeValue
(env, ctx, asm);
                ensureString
(env, ctx, asm);
               
Type argType[] = {Type.tString};
                f
= sbClsDef.matchMethod(env, sourceClass, idInit, argType);
           
}
           
asm.add(where, opc_invokespecial, f);
       
} else {
           
// append this item to the string buffer
            codeValue
(env, ctx, asm);
           
// FIX FOR 4071548
           
// 'StringBuffer.append' converts its argument as if by
           
// 'valueOf', treating character arrays specially.  This
           
// violates JLS 15.17.1.1, which requires that concatenation
           
// convert non-primitive arguments using 'toString'.  We force
           
// the treatment of all reference types as type 'Object', thus
           
// invoking an overloading of 'append' that has the required
           
// semantics.
           
Type argType[] =
               
{ (type.inMask(TM_REFERENCE) && type != Type.tString)
                 
? Type.tObject
                 
: type };
            f
= sbClsDef.matchMethod(env, sourceClass, idAppend, argType);
           
asm.add(where, opc_invokevirtual, f);
       
}
   
}

   
/**
     * Code
     */

   
void codeDup(Environment env, Context ctx, Assembler asm, int items, int depth) {
       
switch (items) {
         
case 0:
           
return;

         
case 1:
           
switch (depth) {
             
case 0:
               
asm.add(where, opc_dup);
               
return;
             
case 1:
               
asm.add(where, opc_dup_x1);
               
return;
             
case 2:
               
asm.add(where, opc_dup_x2);
               
return;

           
}
           
break;
         
case 2:
           
switch (depth) {
             
case 0:
               
asm.add(where, opc_dup2);
               
return;
             
case 1:
               
asm.add(where, opc_dup2_x1);
               
return;
             
case 2:
               
asm.add(where, opc_dup2_x2);
               
return;

           
}
           
break;
       
}
       
throw new CompilerError("can't dup: " + items + ", " + depth);
   
}

   
void codeConversion(Environment env, Context ctx, Assembler asm, Type f, Type t) {
       
int from = f.getTypeCode();
       
int to = t.getTypeCode();

       
switch (to) {
         
case TC_BOOLEAN:
           
if (from != TC_BOOLEAN) {
               
break;
           
}
           
return;
         
case TC_BYTE:
           
if (from != TC_BYTE) {
                codeConversion
(env, ctx, asm, f, Type.tInt);
               
asm.add(where, opc_i2b);
           
}
           
return;
         
case TC_CHAR:
           
if (from != TC_CHAR) {
                codeConversion
(env, ctx, asm, f, Type.tInt);
               
asm.add(where, opc_i2c);
           
}
           
return;
         
case TC_SHORT:
           
if (from != TC_SHORT) {
                codeConversion
(env, ctx, asm, f, Type.tInt);
               
asm.add(where, opc_i2s);
           
}
           
return;
         
case TC_INT:
           
switch (from) {
             
case TC_BYTE:
             
case TC_CHAR:
             
case TC_SHORT:
             
case TC_INT:
               
return;
             
case TC_LONG:
               
asm.add(where, opc_l2i);
               
return;
             
case TC_FLOAT:
               
asm.add(where, opc_f2i);
               
return;
             
case TC_DOUBLE:
               
asm.add(where, opc_d2i);
               
return;
           
}
           
break;
         
case TC_LONG:
           
switch (from) {
             
case TC_BYTE:
             
case TC_CHAR:
             
case TC_SHORT:
             
case TC_INT:
               
asm.add(where, opc_i2l);
               
return;
             
case TC_LONG:
               
return;
             
case TC_FLOAT:
               
asm.add(where, opc_f2l);
               
return;
             
case TC_DOUBLE:
               
asm.add(where, opc_d2l);
               
return;
           
}
           
break;
         
case TC_FLOAT:
           
switch (from) {
             
case TC_BYTE:
             
case TC_CHAR:
             
case TC_SHORT:
             
case TC_INT:
               
asm.add(where, opc_i2f);
               
return;
             
case TC_LONG:
               
asm.add(where, opc_l2f);
               
return;
             
case TC_FLOAT:
               
return;
             
case TC_DOUBLE:
               
asm.add(where, opc_d2f);
               
return;
           
}
           
break;
         
case TC_DOUBLE:
           
switch (from) {
             
case TC_BYTE:
             
case TC_CHAR:
             
case TC_SHORT:
             
case TC_INT:
               
asm.add(where, opc_i2d);
               
return;
             
case TC_LONG:
               
asm.add(where, opc_l2d);
               
return;
             
case TC_FLOAT:
               
asm.add(where, opc_f2d);
               
return;
             
case TC_DOUBLE:
               
return;
           
}
           
break;

         
case TC_CLASS:
           
switch (from) {
             
case TC_NULL:
               
return;
             
case TC_CLASS:
             
case TC_ARRAY:
               
try {
                   
if (!env.implicitCast(f, t)) {
                       
asm.add(where, opc_checkcast, env.getClassDeclaration(t));
                   
}
               
} catch (ClassNotFound e) {
                   
throw new CompilerError(e);
               
}
               
return;
           
}

           
break;

         
case TC_ARRAY:
           
switch (from) {
             
case TC_NULL:
               
return;
             
case TC_CLASS:
             
case TC_ARRAY:
               
try {
                   
if (!env.implicitCast(f, t)) {
                       
asm.add(where, opc_checkcast, t);
                   
}
                   
return;
               
} catch (ClassNotFound e) {
                   
throw new CompilerError(e);
               
}
           
}
           
break;
       
}
       
throw new CompilerError("codeConversion: " + from + ", " + to);
   
}

   
/**
     * Check if the first thing is a constructor invocation
     */

   
public Expression firstConstructor() {
       
return null;
   
}

   
/**
     * Create a copy of the expression for method inlining
     */

   
public Expression copyInline(Context ctx) {
       
return (Expression)clone();
   
}

   
/**
     * Print
     */

   
public void print(PrintStream out) {
       
out.print(opNames[op]);
   
}
}