@@ -864,7 +864,7 @@ public class ClassPool { | |||
*/ | |||
synchronized CtClass makeNestedClass(String classname) { | |||
checkNotFrozen(classname); | |||
CtClass clazz = new CtNewNestedClass(classname, this, false, null); | |||
CtClass clazz = new CtNewClass(classname, this, false, null); | |||
cacheCtClass(classname, clazz, true); | |||
return clazz; | |||
} |
@@ -453,17 +453,46 @@ class CtClassType extends CtClass { | |||
} | |||
public void setModifiers(int mod) { | |||
checkModify(); | |||
updateInnerEntry(mod, getName(), this, true); | |||
ClassFile cf = getClassFile2(); | |||
if (Modifier.isStatic(mod)) { | |||
int flags = cf.getInnerAccessFlags(); | |||
if (flags != -1 && (flags & AccessFlag.STATIC) != 0) | |||
mod = mod & ~Modifier.STATIC; | |||
else | |||
throw new RuntimeException("cannot change " + getName() + " into a static class"); | |||
cf.setAccessFlags(AccessFlag.of(mod & ~Modifier.STATIC)); | |||
} | |||
private static void updateInnerEntry(int newMod, String name, CtClass clazz, boolean outer) { | |||
ClassFile cf = clazz.getClassFile2(); | |||
InnerClassesAttribute ica | |||
= (InnerClassesAttribute)cf.getAttribute(InnerClassesAttribute.tag); | |||
if (ica != null) { | |||
// If the class is a static inner class, its modifier | |||
// does not contain the static bit. Its inner class attribute | |||
// contains the static bit. | |||
int mod = newMod & ~Modifier.STATIC; | |||
int i = ica.find(name); | |||
if (i >= 0) { | |||
int isStatic = ica.accessFlags(i) & AccessFlag.STATIC; | |||
if (isStatic != 0 || !Modifier.isStatic(newMod)) { | |||
clazz.checkModify(); | |||
ica.setAccessFlags(i, AccessFlag.of(mod) | isStatic); | |||
String outName = ica.outerClass(i); | |||
if (outName != null && outer) | |||
try { | |||
CtClass parent = clazz.getClassPool().get(outName); | |||
updateInnerEntry(mod, name, parent, false); | |||
} | |||
catch (NotFoundException e) { | |||
throw new RuntimeException("cannot find the declaring class: " | |||
+ outName); | |||
} | |||
return; | |||
} | |||
} | |||
} | |||
checkModify(); | |||
cf.setAccessFlags(AccessFlag.of(mod)); | |||
if (Modifier.isStatic(newMod)) | |||
throw new RuntimeException("cannot change " + Descriptor.toJavaName(name) | |||
+ " into a static class"); | |||
} | |||
//@Override |
@@ -1,67 +0,0 @@ | |||
/* | |||
* Javassist, a Java-bytecode translator toolkit. | |||
* Copyright (C) 1999- 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, | |||
* or the Apache License Version 2.0. | |||
* | |||
* 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; | |||
import javassist.bytecode.ClassFile; | |||
import javassist.bytecode.AccessFlag; | |||
import javassist.bytecode.InnerClassesAttribute; | |||
/** | |||
* A newly created public nested class. | |||
*/ | |||
class CtNewNestedClass extends CtNewClass { | |||
CtNewNestedClass(String realName, ClassPool cp, boolean isInterface, | |||
CtClass superclass) { | |||
super(realName, cp, isInterface, superclass); | |||
} | |||
/** | |||
* This method does not change the STATIC bit. The original value is kept. | |||
*/ | |||
public void setModifiers(int mod) { | |||
mod = mod & ~Modifier.STATIC; | |||
super.setModifiers(mod); | |||
updateInnerEntry(mod, getName(), this, true); | |||
} | |||
private static void updateInnerEntry(int mod, String name, CtClass clazz, boolean outer) { | |||
ClassFile cf = clazz.getClassFile2(); | |||
InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( | |||
InnerClassesAttribute.tag); | |||
if (ica == null) | |||
return; | |||
int n = ica.tableLength(); | |||
for (int i = 0; i < n; i++) | |||
if (name.equals(ica.innerClass(i))) { | |||
int acc = ica.accessFlags(i) & AccessFlag.STATIC; | |||
ica.setAccessFlags(i, mod | acc); | |||
String outName = ica.outerClass(i); | |||
if (outName != null && outer) | |||
try { | |||
CtClass parent = clazz.getClassPool().get(outName); | |||
updateInnerEntry(mod, name, parent, false); | |||
} | |||
catch (NotFoundException e) { | |||
throw new RuntimeException("cannot find the declaring class: " | |||
+ outName); | |||
} | |||
break; | |||
} | |||
} | |||
} |
@@ -64,8 +64,10 @@ public class InnerClassesAttribute extends AttributeInfo { | |||
/** | |||
* Returns the class name indicated | |||
* by <code>classes[nth].inner_class_info_index</code>. | |||
* The class name is fully-qualified and separated by dot. | |||
* | |||
* @return null or the class name. | |||
* @see ConstPool#getClassInfo(int) | |||
*/ | |||
public String innerClass(int nth) { | |||
int i = innerClassIndex(nth); | |||
@@ -156,6 +158,22 @@ public class InnerClassesAttribute extends AttributeInfo { | |||
ByteArray.write16bit(flags, get(), nth * 8 + 8); | |||
} | |||
/** | |||
* Finds the entry for the given inner class. | |||
* | |||
* @param name the fully-qualified class name separated by dot and $. | |||
* @return the index or -1 if not found. | |||
* @since 3.22 | |||
*/ | |||
public int find(String name) { | |||
int n = tableLength(); | |||
for (int i = 0; i < n; i++) | |||
if (name.equals(innerClass(i))) | |||
return i; | |||
return -1; | |||
} | |||
/** | |||
* Appends a new entry. | |||
* |
@@ -3,6 +3,7 @@ package javassist; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.TypeVariable; | |||
import javassist.bytecode.AccessFlag; | |||
import javassist.bytecode.AnnotationsAttribute; | |||
import javassist.bytecode.AttributeInfo; | |||
import javassist.bytecode.ClassFile; | |||
@@ -291,4 +292,58 @@ public class JvstTest5 extends JvstTestRoot { | |||
Object obj = make(cc.getName()); | |||
assertEquals(3, invoke(obj, "run")); | |||
} | |||
public void testInnerClassModifiers() throws Exception { | |||
CtClass cc = sloader.get("test5.InnerModifier$NonStatic"); | |||
try { | |||
cc.setModifiers(Modifier.PUBLIC | Modifier.STATIC); | |||
fail(); | |||
} | |||
catch (RuntimeException e) { | |||
if (!e.getMessage().startsWith("cannot change ")) | |||
fail(); | |||
} | |||
cc.setModifiers(Modifier.PUBLIC); | |||
cc.writeFile(); | |||
assertEquals(Modifier.PUBLIC, cc.getModifiers()); | |||
InnerClassesAttribute ica = getInnerClassAttr(cc); | |||
int i = ica.find("test5.InnerModifier$NonStatic"); | |||
assertTrue(i >= 0); | |||
assertEquals(Modifier.PUBLIC, ica.accessFlags(i)); | |||
CtClass cc2 = sloader.get("test5.InnerModifier$Static"); | |||
InnerClassesAttribute ica3 = getInnerClassAttr(cc2); | |||
int i3 = ica3.find("test5.InnerModifier$Static"); | |||
assertTrue(i3 >= 0); | |||
assertEquals(AccessFlag.STATIC, ica3.accessFlags(i3)); | |||
cc2.setModifiers(Modifier.PROTECTED | Modifier.STATIC); | |||
cc2.setModifiers(Modifier.PUBLIC); | |||
cc2.writeFile(); | |||
assertEquals(Modifier.PUBLIC | Modifier.STATIC, cc2.getModifiers()); | |||
InnerClassesAttribute ica2 = getInnerClassAttr(cc2); | |||
int i2 = ica2.find("test5.InnerModifier$Static"); | |||
assertTrue(i2 >= 0); | |||
assertEquals(AccessFlag.PUBLIC | AccessFlag.STATIC, ica2.accessFlags(i2)); | |||
CtClass cc3 = cc.getDeclaringClass(); | |||
assertTrue(cc3.isModified()); | |||
cc3.writeFile(); | |||
InnerClassesAttribute ica4 = getInnerClassAttr(cc3); | |||
int i4 = ica4.find("test5.InnerModifier$Static"); | |||
assertTrue(i4 >= 0); | |||
assertEquals(AccessFlag.PUBLIC | AccessFlag.STATIC, ica4.accessFlags(i4)); | |||
int i5 = ica4.find("test5.InnerModifier$NonStatic"); | |||
assertTrue(i5 >= 0); | |||
assertEquals(Modifier.PUBLIC, ica4.accessFlags(i5)); | |||
} | |||
private InnerClassesAttribute getInnerClassAttr(CtClass cc) { | |||
return (InnerClassesAttribute)cc.getClassFile2().getAttribute(InnerClassesAttribute.tag); | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
package test5; | |||
public class InnerModifier { | |||
protected class NonStatic {} | |||
static class Static {} | |||
} |