diff options
31 files changed, 586 insertions, 36 deletions
diff --git a/Readme.html b/Readme.html index 7227b9ba..9ea78d47 100644 --- a/Readme.html +++ b/Readme.html @@ -282,10 +282,14 @@ see javassist.Dump. <h2>Changes</h2> -<p>- version 3.1 +<p>- version 3.1 RC1 <ul> + <li>Better annotation supports. See <code>CtClass.getAnnotations()</code> <li>javassist.tool.HotSwapper was added. + <li>javassist.ClassPool.importPackage() was added. + <li>The compiler now accepts array initializers + (only one dimensional arrays). <li>javassist.Dump was moved to javassist.tool.Dump. </ul> @@ -6,7 +6,7 @@ <project name="javassist" default="jar" basedir="."> - <property name="dist-version" value="javassist-3.1"/> + <property name="dist-version" value="javassist-3.1RC1"/> <property environment="env"/> <property name="target.jar" value="javassist.jar"/> diff --git a/src/main/javassist/CtBehavior.java b/src/main/javassist/CtBehavior.java index 791457c4..a8baaf8c 100644 --- a/src/main/javassist/CtBehavior.java +++ b/src/main/javassist/CtBehavior.java @@ -81,6 +81,22 @@ public abstract class CtBehavior extends CtMember { } /** + * Returns the annotations associated with this method or constructor. + * + * @return an array of annotation-type objects. + * @see CtMember#getAnnotations() + */ + public Object[] getAnnotations() throws ClassNotFoundException { + MethodInfo mi = getMethodInfo2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + mi.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + mi.getAttribute(AnnotationsAttribute.visibleTag); + return CtClassType.toAnnotationType(getDeclaringClass().getClassPool(), + ainfo, ainfo2); + } + + /** * Obtains parameter types of this method/constructor. */ public CtClass[] getParameterTypes() throws NotFoundException { diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index b2d6ff10..a96701b2 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -29,7 +29,15 @@ import javassist.bytecode.Descriptor; import javassist.bytecode.Opcode; import javassist.expr.ExprEditor; -// Subclasses of CtClass: CtClassType, CtPrimitiveType, and CtArray +/* Note: + * + * This class is an abstract class and several methods just return null + * or throw an exception. Those methods are overridden in subclasses + * of this class. Read the source code of CtClassType if you are + * interested in the implementation of Javassist. + * + * Subclasses of CtClass are CtClassType, CtPrimitiveType, and CtArray. + */ /** * An instance of <code>CtClass</code> represents a class. @@ -419,6 +427,19 @@ public abstract class CtClass { } /** + * Returns the annotations associated with this class. + * For example, if an annotation <code>@Author</code> is associated + * with this class, the returned array contains an <code>Author</code> + * object. The member values can be obtained by calling methods on + * the <code>Author</code> object. + * + * @return an array of annotation-type objects. + */ + public Object[] getAnnotations() throws ClassNotFoundException { + return new Object[0]; + } + + /** * Sets the modifiers. * * @param mod modifiers encoded by @@ -872,7 +893,7 @@ public abstract class CtClass { * <p>This is a convenient method mainly for adding * a user-defined attribute. For dealing with attributes, see the * <code>javassist.bytecode</code> package. For example, the following - * expression adds an attribute of a class file. + * expression adds an attribute <code>info</code> to a class file. * * <ul><pre> * getClassFile().addAttribute(info) diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index b400d8b1..d5989406 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -28,6 +28,7 @@ import java.util.Hashtable; import java.util.List; import javassist.bytecode.AccessFlag; import javassist.bytecode.AttributeInfo; +import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.BadBytecode; import javassist.bytecode.Bytecode; import javassist.bytecode.ClassFile; @@ -40,6 +41,7 @@ import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.FieldInfo; import javassist.bytecode.InnerClassesAttribute; import javassist.bytecode.MethodInfo; +import javassist.bytecode.annotation.Annotation; import javassist.compiler.AccessorMaker; import javassist.compiler.CompileError; import javassist.compiler.Javac; @@ -357,6 +359,49 @@ class CtClassType extends CtClass { getClassFile2().setAccessFlags(acc); } + public Object[] getAnnotations() throws ClassNotFoundException { + ClassFile cf = getClassFile2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + cf.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + cf.getAttribute(AnnotationsAttribute.visibleTag); + return toAnnotationType(getClassPool(), ainfo, ainfo2); + } + + static Object[] toAnnotationType(ClassPool cp, AnnotationsAttribute a1, + AnnotationsAttribute a2) throws ClassNotFoundException { + Annotation[] anno1, anno2; + int size1, size2; + + if (a1 == null) { + anno1 = null; + size1 = 0; + } + else { + anno1 = a1.getAnnotations(); + size1 = anno1.length; + } + + if (a2 == null) { + anno2 = null; + size2 = 0; + } + else { + anno2 = a2.getAnnotations(); + size2 = anno2.length; + } + + Object[] result = new Object[size1 + size2]; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + for (int i = 0; i < size1; i++) + result[i] = anno1[i].toAnnotationType(cl, cp); + + for (int j = 0; j < size2; j++) + result[j + size1] = anno2[j].toAnnotationType(cl, cp); + + return result; + } + public boolean subclassOf(CtClass superclass) { if (superclass == null) return false; diff --git a/src/main/javassist/CtField.java b/src/main/javassist/CtField.java index 710f8c50..f4f2986a 100644 --- a/src/main/javassist/CtField.java +++ b/src/main/javassist/CtField.java @@ -218,6 +218,22 @@ public class CtField extends CtMember { } /** + * Returns the annotations associated with this field. + * + * @return an array of annotation-type objects. + * @see CtMember#getAnnotations() + */ + public Object[] getAnnotations() throws ClassNotFoundException { + FieldInfo fi = getFieldInfo2(); + AnnotationsAttribute ainfo = (AnnotationsAttribute) + fi.getAttribute(AnnotationsAttribute.invisibleTag); + AnnotationsAttribute ainfo2 = (AnnotationsAttribute) + fi.getAttribute(AnnotationsAttribute.visibleTag); + return CtClassType.toAnnotationType(getDeclaringClass().getClassPool(), + ainfo, ainfo2); + } + + /** * Returns the type of the field. */ public CtClass getType() throws NotFoundException { diff --git a/src/main/javassist/CtMember.java b/src/main/javassist/CtMember.java index ee8aadbc..d6bd14f1 100644 --- a/src/main/javassist/CtMember.java +++ b/src/main/javassist/CtMember.java @@ -110,6 +110,18 @@ public abstract class CtMember { public abstract void setModifiers(int mod); /** + * Returns the annotations associated with this member. + * For example, if an annotation <code>@Author</code> is associated + * with this member, the returned array contains an <code>Author</code> + * object. The member values can be obtained by calling methods on + * the <code>Author</code> object. + * + * @return an array of annotation-type objects. + * @see CtClass#getAnnotations() + */ + public abstract Object[] getAnnotations() throws ClassNotFoundException; + + /** * Obtains the name of the member. * * <p>As for constructor names, see <code>getName()</code> diff --git a/src/main/javassist/bytecode/annotation/Annotation.java b/src/main/javassist/bytecode/annotation/Annotation.java index f4f8ac61..9971fada 100644 --- a/src/main/javassist/bytecode/annotation/Annotation.java +++ b/src/main/javassist/bytecode/annotation/Annotation.java @@ -17,6 +17,7 @@ package javassist.bytecode.annotation; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; +import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; @@ -252,7 +253,7 @@ public class Annotation { * @return null if the member cannot be found or if the value is * the default value. * - * @see AnnotationDefaultAttribute + * @see javassist.bytecode.AnnotationDefaultAttribute */ public MemberValue getMemberValue(String name) { if (members == null) @@ -267,6 +268,22 @@ public class Annotation { } /** + * Constructs an annotation-type object representing this annotation. + * For example, if this annotation represents <code>@Author</code>, + * this method returns an <code>Author</code> object. + * + * @param cl class loader for loading an annotation type. + * @param cp class pool for obtaining class files. + */ + public Object toAnnotationType(ClassLoader cl, ClassPool cp) + throws ClassNotFoundException + { + return AnnotationImpl.make(cl, + MemberValue.loadClass(cl, getTypeName()), + cp, this); + } + + /** * Writes this annotation. * * @param writer the output. diff --git a/src/main/javassist/bytecode/annotation/AnnotationImpl.java b/src/main/javassist/bytecode/annotation/AnnotationImpl.java new file mode 100644 index 00000000..f81b7b46 --- /dev/null +++ b/src/main/javassist/bytecode/annotation/AnnotationImpl.java @@ -0,0 +1,91 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 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.bytecode.annotation; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.AnnotationDefaultAttribute; +import javassist.bytecode.ClassFile; +import javassist.bytecode.MethodInfo; + +import java.lang.reflect.*; + +class AnnotationImpl implements InvocationHandler { + private Annotation annotation; + private ClassPool pool; + private ClassLoader classLoader; + + /** + * Constructs an annotation object. + * + * @param cl class loader for obtaining annotation types. + * @param clazz the annotation type. + * @param cp class pool for containing an annotation + * type (or null). + * @param anon the annotation. + */ + public static Object make(ClassLoader cl, Class clazz, ClassPool cp, + Annotation anon) { + AnnotationImpl handler = new AnnotationImpl(anon, cp, cl); + return Proxy.newProxyInstance(cl, new Class[] { clazz }, handler); + } + + private AnnotationImpl(Annotation a, ClassPool cp, ClassLoader loader) { + annotation = a; + pool = cp; + classLoader = loader; + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable + { + String name = method.getName(); + MemberValue mv = annotation.getMemberValue(name); + if (mv == null) + return getDefault(name); + else + return mv.getValue(classLoader, pool); + } + + private Object getDefault(String name) + throws ClassNotFoundException, RuntimeException + { + String classname = annotation.getTypeName(); + if (pool != null) + try { + CtClass cc = pool.get(classname); + ClassFile cf = cc.getClassFile2(); + MethodInfo minfo = cf.getMethod(name); + if (minfo != null) { + AnnotationDefaultAttribute ainfo + = (AnnotationDefaultAttribute) + minfo.getAttribute(AnnotationDefaultAttribute.tag); + if (ainfo != null) { + MemberValue mv = ainfo.getDefaultValue(); + return mv.getValue(classLoader, pool); + } + } + } + catch (NotFoundException e) { + throw new RuntimeException("cannot find a class file: " + + classname); + } + + throw new RuntimeException("no default value: " + classname + "." + + name + "()"); + } +} diff --git a/src/main/javassist/bytecode/annotation/AnnotationMemberValue.java b/src/main/javassist/bytecode/annotation/AnnotationMemberValue.java index 92b3e2d4..73da1637 100644 --- a/src/main/javassist/bytecode/annotation/AnnotationMemberValue.java +++ b/src/main/javassist/bytecode/annotation/AnnotationMemberValue.java @@ -14,6 +14,7 @@ */ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -42,6 +43,19 @@ public class AnnotationMemberValue extends MemberValue { value = a; } + Object getValue(ClassLoader cl, ClassPool cp) + throws ClassNotFoundException + { + return AnnotationImpl.make(cl, getType(cl), cp, value); + } + + Class getType(ClassLoader cl) throws ClassNotFoundException { + if (value == null) + throw new ClassNotFoundException("no type specified"); + else + return loadClass(cl, value.getTypeName()); + } + /** * Obtains the value. */ diff --git a/src/main/javassist/bytecode/annotation/ArrayMemberValue.java b/src/main/javassist/bytecode/annotation/ArrayMemberValue.java index 351ffc56..dd586bb3 100644 --- a/src/main/javassist/bytecode/annotation/ArrayMemberValue.java +++ b/src/main/javassist/bytecode/annotation/ArrayMemberValue.java @@ -14,8 +14,10 @@ */ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; +import java.lang.reflect.Array; /** * Array member. @@ -47,6 +49,26 @@ public class ArrayMemberValue extends MemberValue { values = null; } + Object getValue(ClassLoader cl, ClassPool cp) throws ClassNotFoundException { + if (type == null || values == null) + throw new ClassNotFoundException("no array elements specified"); + + int size = values.length; + Object a = Array.newInstance(type.getType(cl), size); + for (int i = 0; i < size; i++) + Array.set(a, i, values[i].getValue(cl, cp)); + + return a; + } + + Class getType(ClassLoader cl) throws ClassNotFoundException { + if (type == null) + throw new ClassNotFoundException("no array type specified"); + + Object a = Array.newInstance(type.getType(cl), 0); + return a.getClass(); + } + /** * Obtains the type of the elements. * diff --git a/src/main/javassist/bytecode/annotation/BooleanMemberValue.java b/src/main/javassist/bytecode/annotation/BooleanMemberValue.java index 4f6507e5..7df117d7 100644 --- a/src/main/javassist/bytecode/annotation/BooleanMemberValue.java +++ b/src/main/javassist/bytecode/annotation/BooleanMemberValue.java @@ -14,6 +14,7 @@ */ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -55,6 +56,14 @@ public class BooleanMemberValue extends MemberValue { setValue(false); } + Object getValue(ClassLoader cl, ClassPool cp) { + return new Boolean(getValue()); + } + + Class getType(ClassLoader cl) { + return boolean.class; + } + /** * Obtains the value of the member. */ diff --git a/src/main/javassist/bytecode/annotation/ByteMemberValue.java b/src/main/javassist/bytecode/annotation/ByteMemberValue.java index 043f1d21..3b892010 100644 --- a/src/main/javassist/bytecode/annotation/ByteMemberValue.java +++ b/src/main/javassist/bytecode/annotation/ByteMemberValue.java @@ -14,6 +14,7 @@ */ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -55,6 +56,14 @@ public class ByteMemberValue extends MemberValue { setValue((byte)0); } + Object getValue(ClassLoader cl, ClassPool cp) { + return new Byte(getValue()); + } + + Class getType(ClassLoader cl) { + return byte.class; + } + /** * Obtains the value of the member. */ diff --git a/src/main/javassist/bytecode/annotation/CharMemberValue.java b/src/main/javassist/bytecode/annotation/CharMemberValue.java index c01b7b78..8d056d8b 100644 --- a/src/main/javassist/bytecode/annotation/CharMemberValue.java +++ b/src/main/javassist/bytecode/annotation/CharMemberValue.java @@ -15,6 +15,7 @@ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -56,6 +57,14 @@ public class CharMemberValue extends MemberValue { setValue('\0'); } + Object getValue(ClassLoader cl, ClassPool cp) { + return new Character(getValue()); + } + + Class getType(ClassLoader cl) { + return char.class; + } + /** * Obtains the value of the member. */ diff --git a/src/main/javassist/bytecode/annotation/ClassMemberValue.java b/src/main/javassist/bytecode/annotation/ClassMemberValue.java index 90da2c41..f5f2fe67 100644 --- a/src/main/javassist/bytecode/annotation/ClassMemberValue.java +++ b/src/main/javassist/bytecode/annotation/ClassMemberValue.java @@ -15,12 +15,13 @@ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import java.io.IOException; /** - * String constant value. + * Class value. * * @author <a href="mailto:bill@jboss.org">Bill Burke</a> * @author Shigeru Chiba @@ -29,7 +30,7 @@ public class ClassMemberValue extends MemberValue { int valueIndex; /** - * Constructs a string constant value. The initial value is specified + * Constructs a class value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Utf8_info structure. @@ -40,7 +41,7 @@ public class ClassMemberValue extends MemberValue { } /** - * Constructs a string constant value. + * Constructs a class value. * * @param className the initial value. */ @@ -50,7 +51,7 @@ public class ClassMemberValue extends MemberValue { } /** - * Constructs a string constant value. + * Constructs a class value. * The initial value is java.lang.Class. */ public ClassMemberValue(ConstPool cp) { @@ -58,6 +59,16 @@ public class ClassMemberValue extends MemberValue { setValue("java.lang.Class"); } + Object getValue(ClassLoader cl, ClassPool cp) + throws ClassNotFoundException + { + return loadClass(cl, getValue()); + } + + Class getType(ClassLoader cl) throws ClassNotFoundException { + return loadClass(cl, "java.lang.Class"); + } + /** * Obtains the value of the member. * diff --git a/src/main/javassist/bytecode/annotation/DoubleMemberValue.java b/src/main/javassist/bytecode/annotation/DoubleMemberValue.java index 131e35f8..7c987875 100644 --- a/src/main/javassist/bytecode/annotation/DoubleMemberValue.java +++ b/src/main/javassist/bytecode/annotation/DoubleMemberValue.java @@ -15,6 +15,7 @@ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -23,7 +24,7 @@ import java.io.IOException; * * @author <a href="mailto:bill@jboss.org">Bill Burke</a> * @author Shigeru Chiba - * @version $Revision: 1.5 $ + * @version $Revision: 1.6 $ */ public class DoubleMemberValue extends MemberValue { int valueIndex; @@ -57,6 +58,14 @@ public class DoubleMemberValue extends MemberValue { setValue(0.0); } + Object getValue(ClassLoader cl, ClassPool cp) { + return new Double(getValue()); + } + + Class getType(ClassLoader cl) { + return double.class; + } + /** * Obtains the value of the member. */ diff --git a/src/main/javassist/bytecode/annotation/EnumMemberValue.java b/src/main/javassist/bytecode/annotation/EnumMemberValue.java index cf6263a7..77e07c27 100644 --- a/src/main/javassist/bytecode/annotation/EnumMemberValue.java +++ b/src/main/javassist/bytecode/annotation/EnumMemberValue.java @@ -16,6 +16,8 @@ package javassist.bytecode.annotation; import java.io.IOException; + +import javassist.ClassPool; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; @@ -52,6 +54,24 @@ public class EnumMemberValue extends MemberValue { typeIndex = valueIndex = 0; } + Object getValue(ClassLoader cl, ClassPool cp) + throws ClassNotFoundException + { + try { + return getType(cl).getField(getValue()).get(null); + } + catch (NoSuchFieldException e) { + throw new ClassNotFoundException(getType() + "." + getValue()); + } + catch (IllegalAccessException e) { + throw new ClassNotFoundException(getType() + "." + getValue()); + } + } + + Class getType(ClassLoader cl) throws ClassNotFoundException { + return loadClass(cl, getType()); + } + /** * Obtains the enum type name. * diff --git a/src/main/javassist/bytecode/annotation/FloatMemberValue.java b/src/main/javassist/bytecode/annotation/FloatMemberValue.java index 87e12ab7..622a9420 100644 --- a/src/main/javassist/bytecode/annotation/FloatMemberValue.java +++ b/src/main/javassist/bytecode/annotation/FloatMemberValue.java @@ -15,6 +15,7 @@ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -23,7 +24,7 @@ import java.io.IOException; * * @author <a href="mailto:bill@jboss.org">Bill Burke</a> * @author Shigeru Chiba - * @version $Revision: 1.5 $ + * @version $Revision: 1.6 $ */ public class FloatMemberValue extends MemberValue { int valueIndex; @@ -57,6 +58,14 @@ public class FloatMemberValue extends MemberValue { setValue(0.0F); } + Object getValue(ClassLoader cl, ClassPool cp) { + return new Float(getValue()); + } + + Class getType(ClassLoader cl) { + return float.class; + } + /** * Obtains the value of the member. */ diff --git a/src/main/javassist/bytecode/annotation/IntegerMemberValue.java b/src/main/javassist/bytecode/annotation/IntegerMemberValue.java index e8846276..949f3752 100644 --- a/src/main/javassist/bytecode/annotation/IntegerMemberValue.java +++ b/src/main/javassist/bytecode/annotation/IntegerMemberValue.java @@ -15,6 +15,7 @@ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -62,6 +63,14 @@ public class IntegerMemberValue extends MemberValue { setValue(0); } + Object getValue(ClassLoader cl, ClassPool cp) { + return new Integer(getValue()); + } + + Class getType(ClassLoader cl) { + return int.class; + } + /** * Obtains the value of the member. */ diff --git a/src/main/javassist/bytecode/annotation/LongMemberValue.java b/src/main/javassist/bytecode/annotation/LongMemberValue.java index 4842792f..a0dd13ad 100644 --- a/src/main/javassist/bytecode/annotation/LongMemberValue.java +++ b/src/main/javassist/bytecode/annotation/LongMemberValue.java @@ -15,6 +15,7 @@ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -56,6 +57,14 @@ public class LongMemberValue extends MemberValue { setValue(0L); } + Object getValue(ClassLoader cl, ClassPool cp) { + return new Long(getValue()); + } + + Class getType(ClassLoader cl) { + return long.class; + } + /** * Obtains the value of the member. */ diff --git a/src/main/javassist/bytecode/annotation/MemberValue.java b/src/main/javassist/bytecode/annotation/MemberValue.java index 84d0eb38..8ee6ce9a 100644 --- a/src/main/javassist/bytecode/annotation/MemberValue.java +++ b/src/main/javassist/bytecode/annotation/MemberValue.java @@ -15,6 +15,7 @@ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -35,6 +36,21 @@ public abstract class MemberValue { } /** + * Returns the value. If the value type is a primitive type, the + * returned value is boxed. + */ + abstract Object getValue(ClassLoader cl, ClassPool cp) + throws ClassNotFoundException; + + abstract Class getType(ClassLoader cl) throws ClassNotFoundException; + + static Class loadClass(ClassLoader cl, String classname) + throws ClassNotFoundException + { + return Class.forName(classname, true, cl); + } + + /** * Accepts a visitor. */ public abstract void accept(MemberValueVisitor visitor); diff --git a/src/main/javassist/bytecode/annotation/ShortMemberValue.java b/src/main/javassist/bytecode/annotation/ShortMemberValue.java index fc0a1702..7d38f4b4 100644 --- a/src/main/javassist/bytecode/annotation/ShortMemberValue.java +++ b/src/main/javassist/bytecode/annotation/ShortMemberValue.java @@ -15,6 +15,7 @@ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -56,6 +57,14 @@ public class ShortMemberValue extends MemberValue { setValue((short)0); } + Object getValue(ClassLoader cl, ClassPool cp) { + return new Short(getValue()); + } + + Class getType(ClassLoader cl) { + return short.class; + } + /** * Obtains the value of the member. */ diff --git a/src/main/javassist/bytecode/annotation/StringMemberValue.java b/src/main/javassist/bytecode/annotation/StringMemberValue.java index 4a32e433..80cb5e3d 100644 --- a/src/main/javassist/bytecode/annotation/StringMemberValue.java +++ b/src/main/javassist/bytecode/annotation/StringMemberValue.java @@ -15,6 +15,7 @@ package javassist.bytecode.annotation; +import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; @@ -56,6 +57,14 @@ public class StringMemberValue extends MemberValue { setValue(""); } + Object getValue(ClassLoader cl, ClassPool cp) { + return getValue(); + } + + Class getType(ClassLoader cl) { + return String.class; + } + /** * Obtains the value of the member. */ diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java index 9b401804..6739c17a 100644 --- a/src/main/javassist/compiler/CodeGen.java +++ b/src/main/javassist/compiler/CodeGen.java @@ -709,13 +709,15 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { */ ASTree init = d.getInitializer(); if (init != null) { - doTypeCheck(init); + doTypeCheck(init); atVariableAssign(null, '=', null, d, init, false); } } public abstract void atNewExpr(NewExpr n) throws CompileError; + public abstract void atArrayInit(ArrayInit init) throws CompileError; + public void atAssignExpr(AssignExpr expr) throws CompileError { atAssignExpr(expr, true); } @@ -770,7 +772,11 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { if (op != '=') atVariable(var); - atAssignCore(expr, op, right, varType, varArray, varClass); + // expr is null if the caller is atDeclarator(). + if (expr == null && right instanceof ArrayInit) + atArrayVariableAssign((ArrayInit)right, varType, varArray, varClass); + else + atAssignCore(expr, op, right, varType, varArray, varClass); if (doDup) if (is2word(varType, varArray)) @@ -796,6 +802,9 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { className = varClass; } + protected abstract void atArrayVariableAssign(ArrayInit init, + int varType, int varArray, String varClass) throws CompileError; + private void atArrayAssign(Expr expr, int op, Expr array, ASTree right, boolean doDup) throws CompileError { diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index 88023202..ae586d68 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -18,6 +18,7 @@ package javassist.compiler; import javassist.*; import javassist.bytecode.*; import javassist.compiler.ast.*; + import java.util.ArrayList; /* Code generator methods depending on javassist.* classes. @@ -29,8 +30,7 @@ public class MemberCodeGen extends CodeGen { protected boolean resultStatic; - public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) - { + public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) { super(b); resolver = new MemberResolver(cp); thisClass = cc; @@ -239,26 +239,46 @@ public class MemberCodeGen extends CodeGen { } public void atNewArrayExpr(NewExpr expr) throws CompileError { - if (expr.getInitializer() != null) - throw new CompileError("array initializer is not supported"); - int type = expr.getArrayType(); ASTList size = expr.getArraySize(); ASTList classname = expr.getClassName(); + ArrayInit init = expr.getInitializer(); if (size.length() > 1) { + if (init != null) + throw new CompileError( + "sorry, multi-dimensional array initializer " + + "for new is not supported"); + atMultiNewArray(type, classname, size); return; } - size.head().accept(this); - exprType = type; - arrayDim = 1; + ASTree sizeExpr = size.head(); + atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init); + } + + private void atNewArrayExpr2(int type, ASTree sizeExpr, + String jvmClassname, ArrayInit init) throws CompileError { + if (init == null) + if (sizeExpr == null) + throw new CompileError("no array size"); + else + sizeExpr.accept(this); + else + if (sizeExpr == null) { + int s = init.length(); + bytecode.addIconst(s); + } + else + throw new CompileError("unnecessary array size specified for new"); + + String elementClass; if (type == CLASS) { - className = resolveClassName(classname); - bytecode.addAnewarray(MemberResolver.jvmToJavaName(className)); + elementClass = resolveClassName(jvmClassname); + bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass)); } else { - className = null; + elementClass = null; int atype = 0; switch (type) { case BOOLEAN : @@ -293,12 +313,40 @@ public class MemberCodeGen extends CodeGen { bytecode.addOpcode(NEWARRAY); bytecode.add(atype); } + + if (init != null) { + int s = init.length(); + ASTList list = init; + for (int i = 0; i < s; i++) { + bytecode.addOpcode(DUP); + bytecode.addIconst(i); + list.head().accept(this); + if (!isRefType(type)) + atNumCastExpr(exprType, type); + + bytecode.addOpcode(getArrayWriteOp(type, 0)); + list = list.tail(); + } + } + + exprType = type; + arrayDim = 1; + className = elementClass; } private static void badNewExpr() throws CompileError { throw new CompileError("bad new expression"); } + protected void atArrayVariableAssign(ArrayInit init, int varType, + int varArray, String varClass) throws CompileError { + atNewArrayExpr2(varType, null, varClass, init); + } + + public void atArrayInit(ArrayInit init) throws CompileError { + throw new CompileError("array initializer is not supported"); + } + protected void atMultiNewArray(int type, ASTList classname, ASTList size) throws CompileError { diff --git a/src/main/javassist/compiler/Parser.java b/src/main/javassist/compiler/Parser.java index e1a0d5b2..5b61ae42 100644 --- a/src/main/javassist/compiler/Parser.java +++ b/src/main/javassist/compiler/Parser.java @@ -660,11 +660,22 @@ public final class Parser implements TokenId { /* array.initializer : * '{' (( array.initializer | expression ) ',')* '}' */ - private ASTree parseArrayInitializer(SymbolTable tbl) + private ArrayInit parseArrayInitializer(SymbolTable tbl) throws CompileError { lex.get(); // '{' - throw new CompileError("array initializer is not supported", lex); + ASTree expr = parseExpression(tbl); + ArrayInit init = new ArrayInit(expr); + while (lex.lookAhead() == ',') { + lex.get(); + expr = parseExpression(tbl); + ASTList.append(init, expr); + } + + if (lex.get() != '}') + throw new SyntaxError(lex); + + return init; } /* par.expression : '(' expression ')' @@ -1250,7 +1261,7 @@ public final class Parser implements TokenId { * | primitive.type array.size [ array.initializer ] */ private NewExpr parseNew(SymbolTable tbl) throws CompileError { - ASTree init = null; + ArrayInit init = null; int t = lex.lookAhead(); if (isBuiltinType(t)) { lex.get(); diff --git a/src/main/javassist/compiler/TypeChecker.java b/src/main/javassist/compiler/TypeChecker.java index 543ecdf8..56417922 100644 --- a/src/main/javassist/compiler/TypeChecker.java +++ b/src/main/javassist/compiler/TypeChecker.java @@ -106,10 +106,17 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { int type = expr.getArrayType(); ASTList size = expr.getArraySize(); ASTList classname = expr.getClassName(); + ASTree init = expr.getInitializer(); + if (init != null) + init.accept(this); + if (size.length() > 1) atMultiNewArray(type, classname, size); else { - size.head().accept(this); + ASTree sizeExpr = size.head(); + if (sizeExpr != null) + sizeExpr.accept(this); + exprType = type; arrayDim = 1; if (type == CLASS) @@ -119,6 +126,16 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { } } + public void atArrayInit(ArrayInit init) throws CompileError { + ASTList list = init; + while (list != null) { + ASTree h = list.head(); + list = list.tail(); + if (h != null) + h.accept(this); + } + } + protected void atMultiNewArray(int type, ASTList classname, ASTList size) throws CompileError { diff --git a/src/main/javassist/compiler/ast/ArrayInit.java b/src/main/javassist/compiler/ast/ArrayInit.java new file mode 100644 index 00000000..bdc35a75 --- /dev/null +++ b/src/main/javassist/compiler/ast/ArrayInit.java @@ -0,0 +1,15 @@ +package javassist.compiler.ast; + +import javassist.compiler.CompileError; +/** + * Array initializer such as <code>{ 1, 2, 3 }</code>. + */ +public class ArrayInit extends ASTList { + public ArrayInit(ASTree firstElement) { + super(firstElement); + } + + public void accept(Visitor v) throws CompileError { v.atArrayInit(this); } + + public String getTag() { return "array"; } +} diff --git a/src/main/javassist/compiler/ast/NewExpr.java b/src/main/javassist/compiler/ast/NewExpr.java index 1a1a3f08..b570ea0b 100644 --- a/src/main/javassist/compiler/ast/NewExpr.java +++ b/src/main/javassist/compiler/ast/NewExpr.java @@ -31,7 +31,7 @@ public class NewExpr extends ASTList implements TokenId { arrayType = CLASS; } - public NewExpr(int type, ASTList arraySize, ASTree init) { + public NewExpr(int type, ASTList arraySize, ArrayInit init) { super(null, new ASTList(arraySize)); newArray = true; arrayType = type; @@ -40,7 +40,7 @@ public class NewExpr extends ASTList implements TokenId { } public static NewExpr makeObjectArray(ASTList className, - ASTList arraySize, ASTree init) { + ASTList arraySize, ArrayInit init) { NewExpr e = new NewExpr(className, arraySize); e.newArray = true; if (init != null) @@ -61,12 +61,12 @@ public class NewExpr extends ASTList implements TokenId { public ASTList getArraySize() { return getArguments(); } - public ASTree getInitializer() { + public ArrayInit getInitializer() { ASTree t = getRight().getRight(); if (t == null) return null; else - return t.getLeft(); + return (ArrayInit)t.getLeft(); } public void accept(Visitor v) throws CompileError { v.atNewExpr(this); } diff --git a/src/main/javassist/compiler/ast/Visitor.java b/src/main/javassist/compiler/ast/Visitor.java index f8af3198..5c361d8d 100644 --- a/src/main/javassist/compiler/ast/Visitor.java +++ b/src/main/javassist/compiler/ast/Visitor.java @@ -47,4 +47,5 @@ public class Visitor { public void atStringL(StringL n) throws CompileError {} public void atIntConst(IntConst n) throws CompileError {} public void atDoubleConst(DoubleConst n) throws CompileError {} + public void atArrayInit(ArrayInit n) throws CompileError {} } diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html index 5befd301..31df6720 100644 --- a/tutorial/tutorial2.html +++ b/tutorial/tutorial2.html @@ -19,6 +19,7 @@ <br><li><a href="#alter">Altering a method body</a> <br><li><a href="#add">Adding a new method or field</a> <br><li><a href="#runtime">Runtime support classes</a> +<br><li><a href="#annotation">Annotations</a> <br><li><a href="#import">Import</a> <br><li><a href="#limit">Limitations</a> </ul> @@ -1440,11 +1441,73 @@ or <code>removeMethod()</code> in <code>CtClass</code>. A <code>CtConstructor</code> can be removed by <code>removeConstructor()</code> in <code>CtClass</code>. +<p><br> + +<a name="annotation"> +<h3>4.4 Annotations</h3> + +<p><code>CtClass</code>, <code>CtMethod</code>, <code>CtField</code> +and <code>CtConstructor</code> provides a convenient method +<code>getAnnotations()</code> for reading annotations. +It returns an annotation-type object. + +<p>For example, suppose the following annotation: + +<ul><pre> +public @interface Author { + String name(); + int year(); +} +</pre></ul> + +<p>This annotation is used as the following: + +<ul><pre> +@Author(name="Chiba", year=2005) +public class Point { + int x, y; +} +</pre></ul> + +<p>Then, the value of the annotation can be obtained by +<code>getAnnotations()</code>. +It returns an array containing +annotation-type objects. + +<ul><pre> +CtClass cc = ClassPool.getDefault().get("Point"); +Object[] all = cc.getAnnotations(); +Author a = (Author)all[0]; +String name = a.name(); +int year = a.year(); +System.out.println("name: " + name + ", year: " + year); +</pre></ul> + +<p>This code snippet should print: + +<ul><pre> +name: Chiba, year: 2005 +</pre></ul> + +<p> +Since the annoation of <code>Point</code> is only <code>@Author</code>, +the length of the array <code>all</code> is one +and <code>all[0]</code> is an <code>Author</code> object. +The member values of the annotation can be obtained +by calling <code>name()</code> and <code>year()</code> +on the <code>Author</code> object. + +<p>To use <code>getAnnotations()</code>, annotation types +such as <code>Author</code> must be included in the current +class path. <em>They must be also accessible from a +<code>ClassPool</code> object.</em> If the class file of an annotation +type is not found, Javassist cannot obtain the default values +of the members of that annotation type. <p><br> <a name="runtime"> -<h3>4.4 Runtime support classes</h3> +<h3>4.5 Runtime support classes</h3> <p>In most cases, a class modified by Javassist does not require Javassist to run. However, some kinds of bytecode generated by the @@ -1458,7 +1521,7 @@ Javassist classes are never used at runtime of the modified classes. <p><br> <a name="import"> -<h3>4.5 Import</h3> +<h3>4.6 Import</h3> <p>All the class names in source code must be fully qualified (they must include package names). @@ -1494,7 +1557,7 @@ must be always a fully qualified name. <p><br> <a name="limit"> -<h3>4.6 Limitations</h3> +<h3>4.7 Limitations</h3> <p>In the current implementation, the Java compiler included in Javassist has several limitations with respect to the language that the compiler can @@ -1507,7 +1570,7 @@ See the <code>javassist.bytecode.annotation</code> package. <p><li>Array initializers, a comma-separated list of expressions enclosed by braces <code>{</code> and <code>}</code>, are not -supported. +available unless the array dimension is one. <p><li>Inner classes or anonymous classes are not supported. |