|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public |
|
class Expression extends Node { |
|
Type type; |
|
|
|
|
|
|
|
*/ |
|
Expression(int op, long where, Type type) { |
|
super(op, where); |
|
this.type = type; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Expression getImplementation() { |
|
return this; |
|
} |
|
|
|
public Type getType() { |
|
return type; |
|
} |
|
|
|
|
|
|
|
*/ |
|
int precedence() { |
|
return (op < opPrecedence.length) ? opPrecedence[op] : 100; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Expression order() { |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isConstant() { |
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Object getValue() { |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean isNull() { |
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean isNonNull() { |
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean equalsDefault() { |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
*-----------------------------------------------------*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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) { |
|
|
|
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 |
|
public boolean fitsType(Environment env, Type t) { |
|
return fitsType(env, (Context) null, t); |
|
} |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Vset checkAmbigName(Environment env, Context ctx, Vset vset, Hashtable exp, |
|
UnaryExpression loc) { |
|
return checkValue(env, ctx, vset, exp); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
|
|
cvars.vsFalse = cvars.vsFalse.copy(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Expression eval() { |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Expression simplify() { |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Expression inline(Environment env, Context ctx) { |
|
return null; |
|
} |
|
public Expression inlineValue(Environment env, Context ctx) { |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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. |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int costInline(int thresh, Environment env, Context ctx) { |
|
return 1; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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"); |
|
} |
|
|
|
|
|
|
|
*/ |
|
void ensureString(Environment env, Context ctx, Assembler asm) |
|
throws ClassNotFound, AmbiguousMember |
|
{ |
|
if (type == Type.tString && isNonNull()) { |
|
return; |
|
} |
|
|
|
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. |
|
|
|
if (type.inMask(TM_REFERENCE)) { |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 { |
|
|
|
Type argType[] = {type}; |
|
MemberDefinition f = |
|
stClsDef.matchMethod(env, sourceClass, idValueOf, argType); |
|
asm.add(where, opc_invokestatic, f); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
asm.add(where, opc_dup); |
|
if (equals("")) { |
|
|
|
f = sbClsDef.matchMethod(env, sourceClass, idInit); |
|
} else { |
|
|
|
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 { |
|
|
|
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 |
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Expression firstConstructor() { |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Expression copyInline(Context ctx) { |
|
return (Expression)clone(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void print(PrintStream out) { |
|
out.print(opNames[op]); |
|
} |
|
} |