blob: 70ec2aae4a73b8e76c79324d9561f5b15a716f6e [file] [log] [blame]
/*
* Copyright (c) 1994, 2003, 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.Assembler;
/**
* 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 AddExpression extends BinaryArithmeticExpression {
/**
* constructor
*/
public AddExpression(long where, Expression left, Expression right) {
super(ADD, where, left, right);
}
/**
* Select the type
*/
void selectType(Environment env, Context ctx, int tm) {
if ((left.type == Type.tString) && !right.type.isType(TC_VOID)) {
type = Type.tString;
return;
} else if ((right.type == Type.tString) && !left.type.isType(TC_VOID)) {
type = Type.tString;
return;
}
super.selectType(env, ctx, tm);
}
public boolean isNonNull() {
// an addition expression cannot yield a null reference as a result
return true;
}
/**
* Evaluate
*/
Expression eval(int a, int b) {
return new IntExpression(where, a + b);
}
Expression eval(long a, long b) {
return new LongExpression(where, a + b);
}
Expression eval(float a, float b) {
return new FloatExpression(where, a + b);
}
Expression eval(double a, double b) {
return new DoubleExpression(where, a + b);
}
Expression eval(String a, String b) {
return new StringExpression(where, a + b);
}
/**
* Inline the value of an AddExpression. If this AddExpression
* represents a concatenation of compile-time constant strings,
* dispatch to the special method inlineValueSB, which handles
* the inlining more efficiently.
*/
public Expression inlineValue(Environment env, Context ctx) {
if (type == Type.tString && isConstant()) {
StringBuffer buffer = inlineValueSB(env, ctx, new StringBuffer());
if (buffer != null) {
// We were able to evaluate the String concatenation.
return new StringExpression(where, buffer.toString());
}
}
// For some reason inlinValueSB() failed to produce a value.
// Use the older, less efficient, inlining mechanism.
return super.inlineValue(env, ctx);
}
/**
* 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().
*
* This method does not use associativity to good effect in
* folding string concatenations. This is room for improvement.
*
* -------------
*
* A bit of history: this method was added because an
* expression like...
*
* "a" + "b" + "c" + "d"
*
* ...was evaluated at compile-time as...
*
* (new StringBuffer((new StringBuffer("a")).append("b").toString())).
* append((new StringBuffer("c")).append("d").toString()).toString()
*
* Alex Garthwaite, in profiling the memory allocation of the
* compiler, noticed this and suggested that the method inlineValueSB()
* be added to evaluate constant string concatenations in a more
* efficient manner. The compiler now builds the string in a
* top-down fashion, by accumulating the result in a StringBuffer
* which is allocated once and passed in as a parameter. The new
* evaluation scheme is equivalent to...
*
* (new StringBuffer("a")).append("b").append("c").append("d")
* .toString()
*
* ...which is more efficient. Since then, the code has been modified
* to fix certain problems. Now, for example, it can return `null'
* when it encounters a concatenation which it is not able to
* evaluate.
*
* See also Expression#inlineValueSB() and ExprExpression#inlineValueSB().
*/
protected StringBuffer inlineValueSB(Environment env,
Context ctx,
StringBuffer buffer) {
if (type != Type.tString) {
// This isn't a concatenation. It is actually an addition
// of some sort. Call the generic inlineValueSB()
return super.inlineValueSB(env, ctx, buffer);
}
buffer = left.inlineValueSB(env, ctx, buffer);
if (buffer != null) {
buffer = right.inlineValueSB(env, ctx, buffer);
}
return buffer;
}
/**
* Simplify
*/
Expression simplify() {
if (!type.isType(TC_CLASS)) {
// Can't simplify floating point add because of -0.0 strangeness
if (type.inMask(TM_INTEGER)) {
if (left.equals(0)) {
return right;
}
if (right.equals(0)) {
return left;
}
}
} else if (right.type.isType(TC_NULL)) {
right = new StringExpression(right.where, "null");
} else if (left.type.isType(TC_NULL)) {
left = new StringExpression(left.where, "null");
}
return this;
}
/**
* The cost of inlining this expression
*/
public int costInline(int thresh, Environment env, Context ctx) {
return (type.isType(TC_CLASS) ? 12 : 1)
+ left.costInline(thresh, env, ctx)
+ right.costInline(thresh, env, ctx);
}
/**
* Code
*/
void codeOperation(Environment env, Context ctx, Assembler asm) {
asm.add(where, opc_iadd + type.getTypeCodeOffset());
}
/**
* 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 {
if (type.isType(TC_CLASS)) {
left.codeAppend(env, ctx, asm, sbClass, needBuffer);
right.codeAppend(env, ctx, asm, sbClass, false);
} else {
super.codeAppend(env, ctx, asm, sbClass, needBuffer);
}
}
public void codeValue(Environment env, Context ctx, Assembler asm) {
if (type.isType(TC_CLASS)) {
try {
// optimize (""+foo) or (foo+"") to String.valueOf(foo)
if (left.equals("")) {
right.codeValue(env, ctx, asm);
right.ensureString(env, ctx, asm);
return;
}
if (right.equals("")) {
left.codeValue(env, ctx, asm);
left.ensureString(env, ctx, asm);
return;
}
ClassDeclaration sbClass =
env.getClassDeclaration(idJavaLangStringBuffer);
ClassDefinition sourceClass = ctx.field.getClassDefinition();
// Create the string buffer and append to it.
codeAppend(env, ctx, asm, sbClass, true);
// Convert the string buffer to a string
MemberDefinition f =
sbClass.getClassDefinition(env).matchMethod(env,
sourceClass,
idToString);
asm.add(where, opc_invokevirtual, f);
} catch (ClassNotFound e) {
throw new CompilerError(e);
} catch (AmbiguousMember e) {
throw new CompilerError(e);
}
} else {
super.codeValue(env, ctx, asm);
}
}
}