From 3b946e08d56d6f0327ef5d501f9f2c5363e0f544 Mon Sep 17 00:00:00 2001 From: chiba Date: Mon, 2 Aug 2004 18:50:41 +0000 Subject: [PATCH] javassist.expr.NewArray has been implemented. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@120 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- Readme.html | 7 +- src/main/javassist/bytecode/Descriptor.java | 29 ++ src/main/javassist/compiler/Javac.java | 8 + src/main/javassist/compiler/JvstCodeGen.java | 16 +- src/main/javassist/expr/ExprEditor.java | 90 +++--- src/main/javassist/expr/NewArray.java | 281 +++++++++++++++++++ tutorial/tutorial2.html | 117 +++++++- 7 files changed, 494 insertions(+), 54 deletions(-) create mode 100644 src/main/javassist/expr/NewArray.java diff --git a/Readme.html b/Readme.html index b968d9d8..a1db7766 100644 --- a/Readme.html +++ b/Readme.html @@ -255,10 +255,13 @@ see javassist.Dump.

Changes

-

- version 3.0 +

- version 3.0 beta 3

- version 3.0 beta in May 18th, 2004. diff --git a/src/main/javassist/bytecode/Descriptor.java b/src/main/javassist/bytecode/Descriptor.java index 9c5b69c7..4a352aaf 100644 --- a/src/main/javassist/bytecode/Descriptor.java +++ b/src/main/javassist/bytecode/Descriptor.java @@ -511,6 +511,35 @@ public class Descriptor { return type; } + /** + * Computes the dimension of the array represented by the given + * descriptor. For example, if the descriptor is "[[I", + * 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 + * "[[Ljava/lang/String;" and the given dimension is 2, + * then this method returns "Ljava/lang/String;". + * + * @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. diff --git a/src/main/javassist/compiler/Javac.java b/src/main/javassist/compiler/Javac.java index 216d0198..67d0cef5 100644 --- a/src/main/javassist/compiler/Javac.java +++ b/src/main/javassist/compiler/Javac.java @@ -323,6 +323,7 @@ public class Javac { * 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. * *

This must be called before calling compileStmnt() and * compileExpr(). The correct value of @@ -332,6 +333,8 @@ public class Javac { * @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. */ @@ -345,14 +348,17 @@ public class Javac { /** * 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. * *

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 @@ -365,6 +371,8 @@ public class Javac { /** * 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); diff --git a/src/main/javassist/compiler/JvstCodeGen.java b/src/main/javassist/compiler/JvstCodeGen.java index 6c78f477..6800a9c8 100644 --- a/src/main/javassist/compiler/JvstCodeGen.java +++ b/src/main/javassist/compiler/JvstCodeGen.java @@ -458,7 +458,7 @@ public class JvstCodeGen extends MemberCodeGen { } /** - * 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. */ @@ -472,15 +472,20 @@ public class JvstCodeGen extends MemberCodeGen { } /** - * 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. */ @@ -499,7 +504,8 @@ public class JvstCodeGen extends MemberCodeGen { paramVarBase = paramBase; useParam0 = use0; - param0Type = MemberResolver.jvmToJavaName(target); + if (target != null) + param0Type = MemberResolver.jvmToJavaName(target); inStaticMethod = isStatic; varNo = paramBase; diff --git a/src/main/javassist/expr/ExprEditor.java b/src/main/javassist/expr/ExprEditor.java index f4ef04eb..8f7cacb8 100644 --- a/src/main/javassist/expr/ExprEditor.java +++ b/src/main/javassist/expr/ExprEditor.java @@ -108,44 +108,57 @@ public class ExprEditor { 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()) { @@ -187,6 +200,15 @@ public class ExprEditor { */ public void edit(NewExpr e) throws CannotCompileException {} + /** + * Edits an expression for array creation (overridable). + * The default implementation performs nothing. + * + * @param a the new expression for creating an array. + * @throws CannotCompileException + */ + public void edit(NewArray a) throws CannotCompileException {} + /** * Edits a method call (overridable). * The default implementation performs nothing. diff --git a/src/main/javassist/expr/NewArray.java b/src/main/javassist/expr/NewArray.java new file mode 100644 index 00000000..db34cf85 --- /dev/null +++ b/src/main/javassist/expr/NewArray.java @@ -0,0 +1,281 @@ +/* + * 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. + * + *

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 int, + * the type returned by this method is + * not int[] but int. + */ + 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. + * + *

$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); + } + + /* $proceed( ..) + */ + 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); + } + } +} diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html index 937c4427..50d24712 100644 --- a/tutorial/tutorial2.html +++ b/tutorial/tutorial2.html @@ -516,6 +516,7 @@ variable is available only in insertAfter() in

The value of $class is an java.lang.Class object representing the class in which the edited method is declared. +This represents the type of $0.

addCatch()

@@ -613,10 +614,13 @@ the formal result type. $class -A java.lang.Class object representing -the class currently edited. +A java.lang.Class object representing +the class that declares the method
+currently edited (the type of $0). +  + @@ -795,8 +799,9 @@ The method replace() in source text representing the substitued statement or block for the field access. +

In the source text, the identifiers starting with $ -have also special meaning: +have special meaning: