<h2>Changes</h2>
-<p>- version 3.0
+<p>- version 3.0 beta 3
<ul>
- <li>CtClass.toClass() has been reimplemented.
+ <li>CtClass.toClass() has been reimplemented. The behavior has been
+ changed.
+ <li>javassist.expr.NewArray has been implemented. It enables modifying
+ an expression for array creation.
</ul>
<p>- version 3.0 beta in May 18th, 2004.
return type;
}
+ /**
+ * Computes the dimension of the array represented by the given
+ * descriptor. For example, if the descriptor is <code>"[[I"</code>,
+ * then this method returns 2.
+ *
+ * @param desc the descriptor.
+ * @return 0 if the descriptor does not represent an array type.
+ */
+ public static int arrayDimension(String desc) {
+ int dim = 0;
+ while (desc.charAt(dim) == '[')
+ ++dim;
+
+ return dim;
+ }
+
+ /**
+ * Returns the descriptor of the type of the array component.
+ * For example, if the given descriptor is
+ * <code>"[[Ljava/lang/String;"</code> and the given dimension is 2,
+ * then this method returns <code>"Ljava/lang/String;"</code>.
+ *
+ * @param desc the descriptor.
+ * @param dim the array dimension.
+ */
+ public static String toArrayComponent(String desc, int dim) {
+ return desc.substring(dim);
+ }
+
/**
* Computes the data size specified by the given descriptor.
* For example, if the descriptor is "D", this method returns 2.
* parameters. $args represents an array of all the parameters.
* It also makes $$ available as a parameter list of method call.
* $0 can represent a local variable other than THIS (variable 0).
+ * $class is also made available.
*
* <p>This must be called before calling <code>compileStmnt()</code> and
* <code>compileExpr()</code>. The correct value of
* @param varNo the register number of $0 (use0 is true)
* or $1 (otherwise).
* @param target the type of $0 (it can be null if use0 is false).
+ * It is used as the name of the type represented
+ * by $class.
* @param isStatic true if the method in which the compiled bytecode
* is embedded is static.
*/
/**
* Prepares to use cast $r, $w, $_, and $type.
+ * $type is made to represent the specified return type.
* It also enables to write a return statement with a return value
* for void method.
*
* <p>If the return type is void, ($r) does nothing.
* The type of $_ is java.lang.Object.
*
+ * @param type the return type.
* @param useResultVar true if $_ is used.
* @return -1 or the variable index assigned to $_.
+ * @see #recordType(CtClass)
*/
public int recordReturnType(CtClass type, boolean useResultVar)
throws CompileError
/**
* Prepares to use $type. Note that recordReturnType() overwrites
* the value of $type.
+ *
+ * @param t the type represented by $type.
*/
public void recordType(CtClass t) {
gen.recordType(t);
}
/**
- * Makes method parameters $0, $1, ..., $args, and $$ available.
+ * Makes method parameters $0, $1, ..., $args, $$, and $class available.
* $0 is equivalent to THIS if the method is not static. Otherwise,
* if the method is static, then $0 is not available.
*/
}
/**
- * Makes method parameters $0, $1, ..., $args, and $$ available.
+ * Makes method parameters $0, $1, ..., $args, $$, and $class available.
* $0 is available only if use0 is true. It might not be equivalent
* to THIS.
*
- * @paaram use0 true if $0 is used.
+ * @param params the parameter types (the types of $1, $2, ..)
+ * @param prefix it must be "$" (the first letter of $0, $1, ...)
+ * @param paramVarName it must be "$args"
+ * @param paramsName it must be "$$"
+ * @param use0 true if $0 is used.
* @param paramBase the register number of $0 (use0 is true)
* or $1 (otherwise).
* @param target the class of $0. If use0 is false, target
- * can be null.
+ * can be null. The value of "target" is also used
+ * as the name of the type represented by $class.
* @param isStatic true if the method in which the compiled bytecode
* is embedded is static.
*/
paramVarBase = paramBase;
useParam0 = use0;
- param0Type = MemberResolver.jvmToJavaName(target);
+ if (target != null)
+ param0Type = MemberResolver.jvmToJavaName(target);
inStaticMethod = isStatic;
varNo = paramBase;
int pos = iterator.next();
int c = iterator.byteAt(pos);
- if (c == Opcode.INVOKESTATIC || c == Opcode.INVOKEINTERFACE
- || c == Opcode.INVOKEVIRTUAL) {
- expr = new MethodCall(pos, iterator, clazz, minfo);
- edit((MethodCall)expr);
- }
- else if (c == Opcode.GETFIELD || c == Opcode.GETSTATIC
- || c == Opcode.PUTFIELD || c == Opcode.PUTSTATIC) {
- expr = new FieldAccess(pos, iterator, clazz, minfo, c);
- edit((FieldAccess)expr);
- }
- else if (c == Opcode.NEW) {
- int index = iterator.u16bitAt(pos + 1);
- newList = new NewOp(newList, pos,
- cp.getClassInfo(index));
- }
- else if (c == Opcode.INVOKESPECIAL) {
- if (newList != null && cp.isConstructor(newList.type,
- iterator.u16bitAt(pos + 1)) > 0) {
- expr = new NewExpr(pos, iterator, clazz, minfo,
- newList.type, newList.pos);
- edit((NewExpr)expr);
- newList = newList.next;
- }
- else {
+ if (c < Opcode.GETSTATIC) // c < 178
+ /* skip */;
+ else if (c < Opcode.NEWARRAY) { // c < 188
+ if (c == Opcode.INVOKESTATIC
+ || c == Opcode.INVOKEINTERFACE
+ || c == Opcode.INVOKEVIRTUAL) {
expr = new MethodCall(pos, iterator, clazz, minfo);
- MethodCall mcall = (MethodCall)expr;
- if (!mcall.getMethodName().equals(
+ edit((MethodCall)expr);
+ }
+ else if (c == Opcode.GETFIELD || c == Opcode.GETSTATIC
+ || c == Opcode.PUTFIELD
+ || c == Opcode.PUTSTATIC) {
+ expr = new FieldAccess(pos, iterator, clazz, minfo, c);
+ edit((FieldAccess)expr);
+ }
+ else if (c == Opcode.NEW) {
+ int index = iterator.u16bitAt(pos + 1);
+ newList = new NewOp(newList, pos,
+ cp.getClassInfo(index));
+ }
+ else if (c == Opcode.INVOKESPECIAL) {
+ if (newList != null && cp.isConstructor(newList.type,
+ iterator.u16bitAt(pos + 1)) > 0) {
+ expr = new NewExpr(pos, iterator, clazz, minfo,
+ newList.type, newList.pos);
+ edit((NewExpr)expr);
+ newList = newList.next;
+ }
+ else {
+ expr = new MethodCall(pos, iterator, clazz, minfo);
+ MethodCall mcall = (MethodCall)expr;
+ if (!mcall.getMethodName().equals(
MethodInfo.nameInit))
- edit(mcall);
+ edit(mcall);
+ }
}
}
- else if (c == Opcode.INSTANCEOF) {
- expr = new Instanceof(pos, iterator, clazz, minfo);
- edit((Instanceof)expr);
- }
- else if (c == Opcode.CHECKCAST) {
- expr = new Cast(pos, iterator, clazz, minfo);
- edit((Cast)expr);
+ else { // c >= 188
+ if (c == Opcode.NEWARRAY || c == Opcode.ANEWARRAY
+ || c == Opcode.MULTIANEWARRAY) {
+ expr = new NewArray(pos, iterator, clazz, minfo, c);
+ edit((NewArray)expr);
+ }
+ else if (c == Opcode.INSTANCEOF) {
+ expr = new Instanceof(pos, iterator, clazz, minfo);
+ edit((Instanceof)expr);
+ }
+ else if (c == Opcode.CHECKCAST) {
+ expr = new Cast(pos, iterator, clazz, minfo);
+ edit((Cast)expr);
+ }
}
if (expr != null && expr.edited()) {
*/
public void edit(NewExpr e) throws CannotCompileException {}
+ /**
+ * Edits an expression for array creation (overridable).
+ * The default implementation performs nothing.
+ *
+ * @param a the <tt>new</tt> expression for creating an array.
+ * @throws CannotCompileException
+ */
+ public void edit(NewArray a) throws CannotCompileException {}
+
/**
* Edits a method call (overridable).
* The default implementation performs nothing.
--- /dev/null
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2004 Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist.expr;
+
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.*;
+import javassist.compiler.ast.ASTList;
+
+/**
+ * Array creation.
+ *
+ * <p>This class does not provide methods for obtaining the initial
+ * values of array elements.
+ */
+public class NewArray extends Expr {
+ int opcode;
+
+ NewArray(int pos, CodeIterator i, CtClass declaring, MethodInfo m,
+ int op) {
+ super(pos, i, declaring, m);
+ opcode = op;
+ }
+
+ /**
+ * Returns the method or constructor containing the array creation
+ * represented by this object.
+ */
+ public CtBehavior where() { return super.where(); }
+
+ /**
+ * Returns the line number of the source line containing the
+ * array creation.
+ *
+ * @return -1 if this information is not available.
+ */
+ public int getLineNumber() {
+ return super.getLineNumber();
+ }
+
+ /**
+ * Returns the source file containing the array creation.
+ *
+ * @return null if this information is not available.
+ */
+ public String getFileName() {
+ return super.getFileName();
+ }
+
+ /**
+ * Returns the list of exceptions that the expression may throw.
+ * This list includes both the exceptions that the try-catch statements
+ * including the expression can catch and the exceptions that
+ * the throws declaration allows the method to throw.
+ */
+ public CtClass[] mayThrow() {
+ return super.mayThrow();
+ }
+
+ /**
+ * Returns the type of array components. If the created array is
+ * a two-dimensional array of <tt>int</tt>,
+ * the type returned by this method is
+ * not <tt>int[]</tt> but <tt>int</tt>.
+ */
+ public CtClass getComponentType() throws NotFoundException {
+ if (opcode == Opcode.NEWARRAY) {
+ int atype = iterator.byteAt(currentPos + 1);
+ return getPrimitiveType(atype);
+ }
+ else if (opcode == Opcode.ANEWARRAY
+ || opcode == Opcode.MULTIANEWARRAY) {
+ int index = iterator.u16bitAt(currentPos + 1);
+ String desc = getConstPool().getClassInfo(index);
+ int dim = Descriptor.arrayDimension(desc);
+ desc = Descriptor.toArrayComponent(desc, dim);
+ return Descriptor.toCtClass(desc, thisClass.getClassPool());
+ }
+ else
+ throw new RuntimeException("bad opcode: " + opcode);
+ }
+
+ CtClass getPrimitiveType(int atype) {
+ switch (atype) {
+ case Opcode.T_BOOLEAN :
+ return CtClass.booleanType;
+ case Opcode.T_CHAR :
+ return CtClass.charType;
+ case Opcode.T_FLOAT :
+ return CtClass.floatType;
+ case Opcode.T_DOUBLE :
+ return CtClass.doubleType;
+ case Opcode.T_BYTE :
+ return CtClass.byteType;
+ case Opcode.T_SHORT :
+ return CtClass.shortType;
+ case Opcode.T_INT :
+ return CtClass.intType;
+ case Opcode.T_LONG :
+ return CtClass.longType;
+ default :
+ throw new RuntimeException("bad atype: " + atype);
+ }
+ }
+
+ /**
+ * Returns the dimension of the created array.
+ */
+ public int getDimension() {
+ if (opcode == Opcode.NEWARRAY)
+ return 1;
+ else if (opcode == Opcode.ANEWARRAY
+ || opcode == Opcode.MULTIANEWARRAY) {
+ int index = iterator.u16bitAt(currentPos + 1);
+ String desc = getConstPool().getClassInfo(index);
+ return Descriptor.arrayDimension(desc)
+ + (opcode == Opcode.ANEWARRAY ? 1 : 0);
+ }
+ else
+ throw new RuntimeException("bad opcode: " + opcode);
+ }
+
+ /**
+ * Returns the number of dimensions of arrays to be created.
+ * If the opcode is multianewarray, this method returns the second
+ * operand. Otherwise, it returns 1.
+ */
+ public int getCreatedDimensions() {
+ if (opcode == Opcode.MULTIANEWARRAY)
+ return iterator.byteAt(currentPos + 3);
+ else
+ return 1;
+ }
+
+ /**
+ * Replaces the array creation with the bytecode derived from
+ * the given source text.
+ *
+ * <p>$0 is available even if the called method is static.
+ * If the field access is writing, $_ is available but the value
+ * of $_ is ignored.
+ *
+ * @param statement a Java statement.
+ */
+ public void replace(String statement) throws CannotCompileException {
+ try {
+ replace2(statement);
+ }
+ catch (CompileError e) { throw new CannotCompileException(e); }
+ catch (NotFoundException e) { throw new CannotCompileException(e); }
+ catch (BadBytecode e) {
+ throw new CannotCompileException("broken method");
+ }
+ }
+
+ private void replace2(String statement)
+ throws CompileError, NotFoundException, BadBytecode,
+ CannotCompileException
+ {
+ ConstPool constPool = getConstPool();
+ int pos = currentPos;
+ CtClass retType;
+ int codeLength;
+ int index = 0;
+ int dim = 1;
+ String desc;
+ if (opcode == Opcode.NEWARRAY) {
+ index = iterator.byteAt(currentPos + 1); // atype
+ CtPrimitiveType cpt = (CtPrimitiveType)getPrimitiveType(index);
+ desc = "[" + cpt.getDescriptor();
+ codeLength = 2;
+ }
+ else if (opcode == Opcode.ANEWARRAY) {
+ index = iterator.u16bitAt(pos + 1);
+ desc = constPool.getClassInfo(index);
+ if (desc.startsWith("["))
+ desc = "[" + desc;
+ else
+ desc = "[L" + desc + ";";
+
+ codeLength = 3;
+ }
+ else if (opcode == Opcode.MULTIANEWARRAY) {
+ index = iterator.u16bitAt(currentPos + 1);
+ desc = constPool.getClassInfo(index);
+ dim = iterator.byteAt(currentPos + 3);
+ codeLength = 4;
+ }
+ else
+ throw new RuntimeException("bad opcode: " + opcode);
+
+ retType = Descriptor.toCtClass(desc, thisClass.getClassPool());
+
+ Javac jc = new Javac(thisClass);
+ CodeAttribute ca = iterator.get();
+
+ CtClass[] params = new CtClass[dim];
+ for (int i = 0; i < dim; ++i)
+ params[i] = CtClass.intType;
+
+ int paramVar = ca.getMaxLocals();
+ jc.recordParams(javaLangObject, params,
+ true, paramVar, withinStatic());
+
+ /* Is $_ included in the source code?
+ */
+ checkResultValue(retType, statement);
+ int retVar = jc.recordReturnType(retType, true);
+ jc.recordProceed(new ProceedForArray(retType, opcode, index, dim));
+
+ Bytecode bytecode = jc.getBytecode();
+ storeStack(params, true, paramVar, bytecode);
+ jc.recordLocalVariables(ca, pos);
+
+ bytecode.addOpcode(ACONST_NULL); // initialize $_
+ bytecode.addAstore(retVar);
+
+ jc.compileStmnt(statement);
+ bytecode.addAload(retVar);
+
+ replace0(pos, bytecode, codeLength);
+ }
+
+ /* <array type> $proceed(<dim> ..)
+ */
+ static class ProceedForArray implements ProceedHandler {
+ CtClass arrayType;
+ int opcode;
+ int index, dimension;
+
+ ProceedForArray(CtClass type, int op, int i, int dim) {
+ arrayType = type;
+ opcode = op;
+ index = i;
+ dimension = dim;
+ }
+
+ public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
+ throws CompileError
+ {
+ int num = gen.getMethodArgsLength(args);
+ if (num != dimension)
+ throw new CompileError(Javac.proceedName
+ + "() with a wrong number of parameters");
+
+ gen.atMethodArgs(args, new int[num],
+ new int[num], new String[num]);
+ bytecode.addOpcode(opcode);
+ if (opcode == Opcode.ANEWARRAY)
+ bytecode.addIndex(index);
+ else if (opcode == Opcode.NEWARRAY)
+ bytecode.add(index);
+ else /* if (opcode == Opcode.MULTIANEWARRAY) */ {
+ bytecode.addIndex(index);
+ bytecode.add(dimension);
+ bytecode.growStack(1 - dimension);
+ }
+
+ gen.setType(arrayType);
+ }
+
+ public void setReturnType(JvstTypeChecker c, ASTList args)
+ throws CompileError
+ {
+ c.setType(arrayType);
+ }
+ }
+}
<p>The value of <code>$class</code> is an <code>java.lang.Class</code>
object representing the class in which the edited method is declared.
+This represents the type of <code>$0</code>.
<h4>addCatch()</h4>
<tr>
<td><code>$class</code></td>
-<td>A <code>java.lang.Class</code> object representing
-the class currently edited.</td>
+<td rowspan=2>A <code>java.lang.Class</code> object representing
+the class that declares the method<br>
+currently edited (the type of $0).</td>
</tr>
+<tr><td> </td></tr>
+
</table>
</ul>
source text representing the substitued statement or
block for the field access.
+<p>
In the source text, the identifiers starting with <code>$</code>
-have also special meaning:
+have special meaning:
<ul><table border=0>
<tr>
<h4>javassist.expr.NewExpr</h4>
<p>A <code>NewExpr</code> object represents object creation
-with the <code>new</code> operator.
+with the <code>new</code> operator (not including array creation).
The method <code>edit()</code> in <code>ExprEditor</code>
receives this object if object creation is found.
The method <code>replace()</code> in
source text representing the substitued statement or
block for the object creation.
+<p>
In the source text, the identifiers starting with <code>$</code>
-have also special meaning:
+have special meaning:
<ul><table border=0>
</td>
</tr>
-<tr><td><code>$class</code>    </td>
-<td>A <code>java.lang.Class</code> object representing
-the class of the created object.
-</td></tr>
-
<tr><td><code>$sig</code>    </td>
<td>An array of <code>java.lang.Class</code> objects representing
the formal parameter types.</td>
</tr>
+<tr><td><code>$type</code>    </td>
+<td>A <code>java.lang.Class</code> object representing
+the class of the created object.
+</td></tr>
+
<tr><td><code>$proceed</code>    </td>
<td>The name of a virtual method executing the original
object creation.
<code>$args</code> and <code>$$</code>
are also available.
+<h4>javassist.expr.NewArray</h4>
+
+<p>A <code>NewArray</code> object represents array creation
+with the <code>new</code> operator.
+The method <code>edit()</code> in <code>ExprEditor</code>
+receives this object if array creation is found.
+The method <code>replace()</code> in
+<code>NewArray</code> receives
+source text representing the substitued statement or
+block for the array creation.
+
+<p>
+In the source text, the identifiers starting with <code>$</code>
+have special meaning:
+
+<ul><table border=0>
+
+<tr>
+<td><code>$0</code></td>
+<td>
+<code>null</code>.
+</td>
+</tr>
+
+<tr>
+<td><code>$1</code>, <code>$2</code>, ...    </td>
+<td>
+The size of each dimension.
+</td>
+</tr>
+
+<tr>
+<td><code>$_</code></td>
+<td rowspan=2>
+The resulting value of the array creation.
+<br>A newly created array must be stored in this variable.
+</td>
+</tr>
+
+<tr><td> </td></tr>
+
+<tr>
+<td><code>$r</code></td>
+<td>
+The type of the created array.
+</td>
+</tr>
+
+<tr><td><code>$type</code>    </td>
+<td>A <code>java.lang.Class</code> object representing
+the class of the created array.
+</td></tr>
+
+<tr><td><code>$proceed</code>    </td>
+<td>The name of a virtual method executing the original
+array creation.
+.</td>
+</tr>
+
+</table>
+</ul>
+
+<p>The other identifiers such as <code>$w</code>,
+<code>$args</code> and <code>$$</code>
+are also available.
+
+<p>For example, if the array creation is the following expression,
+
+<ul><pre>
+String[][] s = new String[3][4];
+</pre></ul>
+
+then the value of $1 and $2 are 3 and 4, respectively. $3 is not available.
+
+<p>If the array creation is the following expression,
+
+<ul><pre>
+String[][] s = new String[3][];
+</pre></ul>
+
+then the value of $1 is 3 but $2 is not available.
+
<h4>javassist.expr.Instanceof</h4>
<p>A <code>Instanceof</code> object represents an <code>instanceof</code>
source text representing the substitued statement or
block for the expression.
+<p>
In the source text, the identifiers starting with <code>$</code>
-have also special meaning:
+have special meaning:
<ul><table border=0>
source text representing the substitued statement or
block for the expression.
+<p>
In the source text, the identifiers starting with <code>$</code>
-have also special meaning:
+have special meaning:
<ul><table border=0>
<code>Handler</code> compiles the received
source text and inserts it at the beginning of the <code>catch</code> clause.
+<p>
In the source text, the identifiers starting with <code>$</code>
-have special meaning:
+have meaning:
<ul><table border=0>