@@ -1400,10 +1400,11 @@ class CtClassType extends CtClass { | |||
int mod = m.getModifiers(); | |||
if ((getModifiers() & Modifier.INTERFACE) != 0) { | |||
m.setModifiers(mod | Modifier.PUBLIC); | |||
if ((mod & Modifier.ABSTRACT) == 0) | |||
if (Modifier.isProtected(mod) || Modifier.isPrivate(mod)) | |||
throw new CannotCompileException( | |||
"an interface method must be abstract: " + m.toString()); | |||
"an interface method must be public: " + m.toString()); | |||
m.setModifiers(mod | Modifier.PUBLIC); | |||
} | |||
getMembers().addMethod(m); |
@@ -213,6 +213,32 @@ public class AnnotationsAttribute extends AttributeInfo { | |||
setAnnotations(newlist); | |||
} | |||
/** | |||
* Removes an annotation by type. | |||
* After removing an annotation, if {@link #numAnnotations()} returns 0, | |||
* this annotations attribute has to be removed. | |||
* | |||
* @param type of annotation to remove | |||
* @return whether an annotation with the given type has been removed | |||
* @since 3.21 | |||
*/ | |||
public boolean removeAnnotation(String type) { | |||
Annotation[] annotations = getAnnotations(); | |||
for (int i = 0; i < annotations.length; i++) { | |||
if (annotations[i].getTypeName().equals(type)) { | |||
Annotation[] newlist = new Annotation[annotations.length - 1]; | |||
System.arraycopy(annotations, 0, newlist, 0, i); | |||
if (i < annotations.length - 1) { | |||
System.arraycopy(annotations, i + 1, newlist, i, | |||
annotations.length - i - 1); | |||
} | |||
setAnnotations(newlist); | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* Parses the annotations and returns a data structure representing | |||
* that parsed annotations. Note that changes of the node values of the |
@@ -231,16 +231,21 @@ public class AttributeInfo { | |||
return null; // no such attribute | |||
} | |||
static synchronized void remove(ArrayList list, String name) { | |||
static synchronized AttributeInfo remove(ArrayList list, String name) { | |||
if (list == null) | |||
return; | |||
return null; | |||
AttributeInfo removed = null; | |||
ListIterator iterator = list.listIterator(); | |||
while (iterator.hasNext()) { | |||
AttributeInfo ai = (AttributeInfo)iterator.next(); | |||
if (ai.getName().equals(name)) | |||
if (ai.getName().equals(name)) { | |||
iterator.remove(); | |||
removed = ai; | |||
} | |||
} | |||
return removed; | |||
} | |||
static void writeAll(ArrayList list, DataOutputStream out) |
@@ -771,6 +771,11 @@ public final class ClassFile { | |||
* Returns the attribute with the specified name. If there are multiple | |||
* attributes with that name, this method returns either of them. It | |||
* returns null if the specified attributed is not found. | |||
* | |||
* <p>An attribute name can be obtained by, for example, | |||
* {@link AnnotationsAttribute#visibleTab} or | |||
* {@link AnnotationsAttribute#invisibleTab}. | |||
* </p> | |||
* | |||
* @param name attribute name | |||
* @see #getAttributes() | |||
@@ -787,6 +792,17 @@ public final class ClassFile { | |||
return null; | |||
} | |||
/** | |||
* Removes an attribute with the specified name. | |||
* | |||
* @param name attribute name. | |||
* @return the removed attribute or null. | |||
* @since 3.21 | |||
*/ | |||
public AttributeInfo removeAttribute(String name) { | |||
return AttributeInfo.remove(attributes, name); | |||
} | |||
/** | |||
* Appends an attribute. If there is already an attribute with the same | |||
* name, the new one substitutes for it. |
@@ -231,6 +231,11 @@ public final class FieldInfo { | |||
* Returns the attribute with the specified name. | |||
* It returns null if the specified attribute is not found. | |||
* | |||
* <p>An attribute name can be obtained by, for example, | |||
* {@link AnnotationsAttribute#visibleTab} or | |||
* {@link AnnotationsAttribute#invisibleTab}. | |||
* </p> | |||
* | |||
* @param name attribute name | |||
* @see #getAttributes() | |||
*/ | |||
@@ -238,6 +243,17 @@ public final class FieldInfo { | |||
return AttributeInfo.lookup(attribute, name); | |||
} | |||
/** | |||
* Removes an attribute with the specified name. | |||
* | |||
* @param name attribute name. | |||
* @return the removed attribute or null. | |||
* @since 3.21 | |||
*/ | |||
public AttributeInfo removeAttribute(String name) { | |||
return AttributeInfo.remove(attribute, name); | |||
} | |||
/** | |||
* Appends an attribute. If there is already an attribute with | |||
* the same name, the new one substitutes for it. |
@@ -315,6 +315,11 @@ public class MethodInfo { | |||
* Returns the attribute with the specified name. If it is not found, this | |||
* method returns null. | |||
* | |||
* <p>An attribute name can be obtained by, for example, | |||
* {@link AnnotationsAttribute#visibleTab} or | |||
* {@link AnnotationsAttribute#invisibleTab}. | |||
* </p> | |||
* | |||
* @param name attribute name | |||
* @return an <code>AttributeInfo</code> object or null. | |||
* @see #getAttributes() | |||
@@ -323,6 +328,17 @@ public class MethodInfo { | |||
return AttributeInfo.lookup(attribute, name); | |||
} | |||
/** | |||
* Removes an attribute with the specified name. | |||
* | |||
* @param name attribute name. | |||
* @return the removed attribute or null. | |||
* @since 3.21 | |||
*/ | |||
public AttributeInfo removeAttribute(String name) { | |||
return AttributeInfo.remove(attribute, name); | |||
} | |||
/** | |||
* Appends an attribute. If there is already an attribute with the same | |||
* name, the new one substitutes for it. |
@@ -71,6 +71,8 @@ public class ControlFlow { | |||
return new Block[size]; | |||
} | |||
}.make(minfo); | |||
if (basicBlocks == null) | |||
basicBlocks = new Block[0]; | |||
int size = basicBlocks.length; | |||
int[] counters = new int[size]; | |||
for (int i = 0; i < size; i++) { | |||
@@ -97,6 +99,9 @@ public class ControlFlow { | |||
/** | |||
* Returns all the basic blocks in the method body. | |||
* | |||
* @return an array of basic blocks, the array has length 0 if | |||
* the method doesn't have code. | |||
*/ | |||
public Block[] basicBlocks() { | |||
return basicBlocks; | |||
@@ -133,7 +138,7 @@ public class ControlFlow { | |||
* For every array element <code>node</code>, its index in the | |||
* array is equivalent to <code>node.block().index()</code>. | |||
* | |||
* @return an array of the tree nodes, or null if the method is abstract. | |||
* @return an array of the tree nodes, or null if the method doesn't have code. | |||
* @see Node#block() | |||
* @see Block#index() | |||
*/ | |||
@@ -179,7 +184,7 @@ public class ControlFlow { | |||
* For every array element <code>node</code>, its index in the | |||
* array is equivalent to <code>node.block().index()</code>. | |||
* | |||
* @return an array of the tree nodes, or null if the method is abstract. | |||
* @return an array of the tree nodes, or null if the method doesn't have code. | |||
* @see Node#block() | |||
* @see Block#index() | |||
*/ |
@@ -1086,7 +1086,8 @@ public class JvstTest3 extends JvstTestRoot { | |||
CtMethod m3 = CtMethod.make("public void foo3() {}", cc); | |||
try { | |||
cc.addMethod(m3); | |||
fail(); | |||
if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_8) | |||
fail(); | |||
} | |||
catch (CannotCompileException e) { | |||
// System.out.println(e); |
@@ -4,6 +4,7 @@ import java.lang.annotation.Annotation; | |||
import java.lang.reflect.TypeVariable; | |||
import javassist.bytecode.AnnotationsAttribute; | |||
import javassist.bytecode.AttributeInfo; | |||
import javassist.bytecode.ClassFile; | |||
import javassist.bytecode.ConstPool; | |||
import javassist.bytecode.InnerClassesAttribute; | |||
@@ -241,5 +242,52 @@ public class JvstTest5 extends JvstTestRoot { | |||
cp.appendClassPath(new LoaderClassPath(new Loader())); | |||
assertNotNull(cp.get(Object.class.getName())); | |||
assertNotNull(cp.get(this.getClass().getName())); | |||
public void testAddDefaultMethod() throws Exception { | |||
CtClass cc = sloader.makeInterface("test5.AddDefaultMethod"); | |||
cc.addMethod(CtNewMethod.make("static int foo() { return 1; }", cc)); | |||
cc.addMethod(CtNewMethod.make("public static int foo1() { return 1; }", cc)); | |||
cc.addMethod(CtNewMethod.make("public int foo2() { return 1; }", cc)); | |||
cc.addMethod(CtNewMethod.make("int foo3() { return 1; }", cc)); | |||
try { | |||
cc.addMethod(CtNewMethod.make("private int foo4() { return 1; }", cc)); | |||
fail(); | |||
} catch (CannotCompileException e) {} | |||
try { | |||
cc.addMethod(CtNewMethod.make("private static int foo5() { return 1; }", cc)); | |||
fail(); | |||
} catch (CannotCompileException e) {} | |||
} | |||
public void testRemoveAnnotatino() throws Exception { | |||
CtClass cc = sloader.get("test5.RemoveAnnotation"); | |||
AnnotationsAttribute aa | |||
= (AnnotationsAttribute)cc.getClassFile().getAttribute(AnnotationsAttribute.invisibleTag); | |||
assertTrue(aa.removeAnnotation("test5.RemoveAnno1")); | |||
AttributeInfo ai = cc.getClassFile().removeAttribute(AnnotationsAttribute.invisibleTag); | |||
assertEquals(ai.getName(), AnnotationsAttribute.invisibleTag); | |||
CtMethod foo = cc.getDeclaredMethod("foo"); | |||
AnnotationsAttribute aa2 = (AnnotationsAttribute)foo.getMethodInfo().getAttribute(AnnotationsAttribute.invisibleTag); | |||
assertTrue(aa2.removeAnnotation("test5.RemoveAnno1")); | |||
CtMethod bar = cc.getDeclaredMethod("bar"); | |||
AnnotationsAttribute aa3 = (AnnotationsAttribute)bar.getMethodInfo().getAttribute(AnnotationsAttribute.invisibleTag); | |||
assertFalse(aa3.removeAnnotation("test5.RemoveAnno1")); | |||
assertTrue(aa3.removeAnnotation("test5.RemoveAnno2")); | |||
AttributeInfo ai2 = bar.getMethodInfo().removeAttribute(AnnotationsAttribute.invisibleTag); | |||
assertEquals(ai2.getName(), AnnotationsAttribute.invisibleTag); | |||
CtMethod run = cc.getDeclaredMethod("run"); | |||
AttributeInfo ai3 = run.getMethodInfo().removeAttribute(AnnotationsAttribute.invisibleTag); | |||
assertNull(ai3); | |||
CtField baz = cc.getDeclaredField("baz"); | |||
AttributeInfo ai4 = baz.getFieldInfo().removeAttribute(AnnotationsAttribute.invisibleTag); | |||
assertEquals(ai4.getName(), AnnotationsAttribute.invisibleTag); | |||
cc.writeFile(); | |||
Object obj = make(cc.getName()); | |||
assertEquals(3, invoke(obj, "run")); | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
package test5; | |||
@interface RemoveAnno1 {} | |||
@interface RemoveAnno2 { | |||
int foo() default 3; | |||
} | |||
@RemoveAnno1 public class RemoveAnnotation { | |||
@RemoveAnno1 @RemoveAnno2(foo=4) | |||
int foo() { return 1; } | |||
@RemoveAnno2 | |||
int bar() { return 2; } | |||
@RemoveAnno1 | |||
int baz = 10; | |||
public int run() { return foo() + bar(); } | |||
} |