a private field declared in an enclosing class. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@98 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -35,7 +35,7 @@ public abstract class CtClass { | |||
/** | |||
* The version number of this release. | |||
*/ | |||
public static final String version = "3.0 alpha 1"; | |||
public static final String version = "3.0 RC1"; | |||
/** | |||
* Prints the version number and the copyright notice. |
@@ -68,6 +68,35 @@ public class AccessFlag { | |||
return (accflags & ~(PROTECTED | PUBLIC | PRIVATE)); | |||
} | |||
/** | |||
* Returns true if the access flags include the public bit. | |||
*/ | |||
public static boolean isPublic(int accflags) { | |||
return (accflags & PUBLIC) != 0; | |||
} | |||
/** | |||
* Returns true if the access flags include the protected bit. | |||
*/ | |||
public static boolean isProtected(int accflags) { | |||
return (accflags & PROTECTED) != 0; | |||
} | |||
/** | |||
* Returns true if the access flags include the private bit. | |||
*/ | |||
public static boolean isPrivate(int accflags) { | |||
return (accflags & PRIVATE) != 0; | |||
} | |||
/** | |||
* Returns true if the access flags include neither public, protected, | |||
* or private. | |||
*/ | |||
public static boolean isPackage(int accflags) { | |||
return (accflags & (PROTECTED | PUBLIC | PRIVATE)) == 0; | |||
} | |||
/** | |||
* Clears a specified bit in <code>accflags</code>. | |||
*/ |
@@ -34,6 +34,7 @@ import javassist.bytecode.annotation.AnnotationGroup; | |||
* @see javassist.CtClass#getClassFile() | |||
*/ | |||
public final class ClassFile { | |||
int major, minor; // version number | |||
ConstPool constPool; | |||
int thisClass; | |||
int accessFlags; | |||
@@ -64,6 +65,8 @@ public final class ClassFile { | |||
*/ | |||
public ClassFile(boolean isInterface, | |||
String classname, String superclass) { | |||
major = 45; | |||
minor = 3; // JDK 1.1 or later | |||
constPool = new ConstPool(classname); | |||
thisClass = constPool.getThisClassInfo(); | |||
if (isInterface) | |||
@@ -535,8 +538,8 @@ public final class ClassFile { | |||
if (magic != 0xCAFEBABE) | |||
throw new IOException("non class file"); | |||
int major = in.readUnsignedShort(); | |||
int minor = in.readUnsignedShort(); | |||
minor = in.readUnsignedShort(); | |||
major = in.readUnsignedShort(); | |||
constPool = new ConstPool(in); | |||
accessFlags = in.readUnsignedShort(); | |||
thisClass = in.readUnsignedShort(); | |||
@@ -578,8 +581,8 @@ public final class ClassFile { | |||
int i, n; | |||
out.writeInt(0xCAFEBABE); // magic | |||
out.writeShort(3); // major version | |||
out.writeShort(45); // minor version | |||
out.writeShort(minor); // minor version | |||
out.writeShort(major); // major version | |||
constPool.write(out); // constant pool | |||
out.writeShort(accessFlags); | |||
out.writeShort(thisClass); |
@@ -45,6 +45,7 @@ public class ClassFileWriter { | |||
int mod | |||
= AccessFlag.toModifier(cf.getAccessFlags() | |||
& ~AccessFlag.SYNCHRONIZED); | |||
out.println("major: " + cf.major + ", minor: " + cf.minor); | |||
out.println(Modifier.toString(mod) + " class " | |||
+ cf.getName() + " extends " + cf.getSuperclass()); | |||
@@ -57,10 +57,7 @@ public class AccessorMaker { | |||
return accName; // already exists. | |||
ClassFile cf = clazz.getClassFile(); // turn on the modified flag. | |||
do { | |||
accName = "access$" + uniqueNumber++; | |||
} while (cf.getMethod(accName) != null); | |||
accName = findAccessorName(cf); | |||
try { | |||
ConstPool cp = cf.getConstPool(); | |||
ClassPool pool = clazz.getClassPool(); | |||
@@ -98,4 +95,118 @@ public class AccessorMaker { | |||
accessors.put(key, accName); | |||
return accName; | |||
} | |||
/** | |||
* Returns the method_info representing the added getter. | |||
*/ | |||
public MethodInfo getFieldGetter(FieldInfo finfo, boolean is_static) | |||
throws CompileError | |||
{ | |||
String fieldName = finfo.getName(); | |||
String key = fieldName + ":getter"; | |||
Object res = accessors.get(key); | |||
if (res != null) | |||
return (MethodInfo)res; // already exists. | |||
ClassFile cf = clazz.getClassFile(); // turn on the modified flag. | |||
String accName = findAccessorName(cf); | |||
try { | |||
ConstPool cp = cf.getConstPool(); | |||
ClassPool pool = clazz.getClassPool(); | |||
String fieldType = finfo.getDescriptor(); | |||
String accDesc; | |||
if (is_static) | |||
accDesc = "()" + fieldType; | |||
else | |||
accDesc = "(" + Descriptor.of(clazz) + ")" + fieldType; | |||
MethodInfo minfo = new MethodInfo(cp, accName, accDesc); | |||
minfo.setAccessFlags(AccessFlag.STATIC); | |||
minfo.addAttribute(new SyntheticAttribute(cp)); | |||
Bytecode code = new Bytecode(cp); | |||
if (is_static) { | |||
code.addGetstatic(Bytecode.THIS, fieldName, fieldType); | |||
} | |||
else { | |||
code.addAload(0); | |||
code.addGetfield(Bytecode.THIS, fieldName, fieldType); | |||
code.setMaxLocals(1); | |||
} | |||
code.addReturn(Descriptor.toCtClass(fieldType, pool)); | |||
minfo.setCodeAttribute(code.toCodeAttribute()); | |||
cf.addMethod(minfo); | |||
accessors.put(key, minfo); | |||
return minfo; | |||
} | |||
catch (CannotCompileException e) { | |||
throw new CompileError(e); | |||
} | |||
catch (NotFoundException e) { | |||
throw new CompileError(e); | |||
} | |||
} | |||
/** | |||
* Returns the method_info representing the added setter. | |||
*/ | |||
public MethodInfo getFieldSetter(FieldInfo finfo, boolean is_static) | |||
throws CompileError | |||
{ | |||
String fieldName = finfo.getName(); | |||
String key = fieldName + ":setter"; | |||
Object res = accessors.get(key); | |||
if (res != null) | |||
return (MethodInfo)res; // already exists. | |||
ClassFile cf = clazz.getClassFile(); // turn on the modified flag. | |||
String accName = findAccessorName(cf); | |||
try { | |||
ConstPool cp = cf.getConstPool(); | |||
ClassPool pool = clazz.getClassPool(); | |||
String fieldType = finfo.getDescriptor(); | |||
String accDesc; | |||
if (is_static) | |||
accDesc = "(" + fieldType + ")V"; | |||
else | |||
accDesc = "(" + Descriptor.of(clazz) + fieldType + ")V"; | |||
MethodInfo minfo = new MethodInfo(cp, accName, accDesc); | |||
minfo.setAccessFlags(AccessFlag.STATIC); | |||
minfo.addAttribute(new SyntheticAttribute(cp)); | |||
Bytecode code = new Bytecode(cp); | |||
int reg; | |||
if (is_static) { | |||
reg = code.addLoad(0, Descriptor.toCtClass(fieldType, pool)); | |||
code.addPutstatic(Bytecode.THIS, fieldName, fieldType); | |||
} | |||
else { | |||
code.addAload(0); | |||
reg = code.addLoad(1, Descriptor.toCtClass(fieldType, pool)) | |||
+ 1; | |||
code.addPutfield(Bytecode.THIS, fieldName, fieldType); | |||
} | |||
code.addReturn(null); | |||
code.setMaxLocals(reg); | |||
minfo.setCodeAttribute(code.toCodeAttribute()); | |||
cf.addMethod(minfo); | |||
accessors.put(key, minfo); | |||
return minfo; | |||
} | |||
catch (CannotCompileException e) { | |||
throw new CompileError(e); | |||
} | |||
catch (NotFoundException e) { | |||
throw new CompileError(e); | |||
} | |||
} | |||
private String findAccessorName(ClassFile cf) { | |||
String accName; | |||
do { | |||
accName = "access$" + uniqueNumber++; | |||
} while (cf.getMethod(accName) != null); | |||
return accName; | |||
} | |||
} |
@@ -1269,13 +1269,20 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
int token = expr.getOperator(); | |||
ASTree oprand = expr.oprand1(); | |||
if (token == '.') | |||
if (((Symbol)expr.oprand2()).get().equals("length")) | |||
if (token == '.') { | |||
String member = ((Symbol)expr.oprand2()).get(); | |||
if (member.equals("length")) | |||
atArrayLength(expr); | |||
else if (member.equals("class")) | |||
atClassObject(expr); // .class | |||
else | |||
atFieldRead(expr); | |||
} | |||
else if (token == MEMBER) { // field read | |||
if (!atClassObject(expr)) // .class | |||
String member = ((Symbol)expr.oprand2()).get(); | |||
if (member.equals("class")) | |||
atClassObject(expr); // .class | |||
else | |||
atFieldRead(expr); | |||
} | |||
else if (token == ARRAY) | |||
@@ -1345,14 +1352,16 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
protected abstract void atFieldRead(ASTree expr) throws CompileError; | |||
public boolean atClassObject(Expr expr) throws CompileError { | |||
if (!((Symbol)expr.oprand2()).get().equals("class")) | |||
return false; | |||
if (resolveClassName((ASTList)expr.oprand1()) == null) | |||
return false; | |||
public void atClassObject(Expr expr) throws CompileError { | |||
ASTree op1 = expr.oprand1(); | |||
String cname; | |||
if (op1 instanceof ASTList) | |||
cname = Declarator.astToClassName((ASTList)op1, '/'); | |||
else | |||
cname = ((Symbol)op1).get(); | |||
throw new CompileError(".class is not supported"); | |||
cname = resolveClassName(cname); | |||
throw new CompileError(".class is not supported: " + cname); | |||
} | |||
public void atArrayLength(Expr expr) throws CompileError { |
@@ -371,7 +371,7 @@ public class MemberCodeGen extends CodeGen { | |||
if (declClass != targetClass) | |||
throw new CompileError("no such a constructor"); | |||
} | |||
else if ((acc & AccessFlag.PRIVATE) != 0) { | |||
else if (AccessFlag.isPrivate(acc)) | |||
if (declClass == thisClass) | |||
isSpecial = true; | |||
else { | |||
@@ -390,7 +390,6 @@ public class MemberCodeGen extends CodeGen { | |||
throw new CompileError("Method " + orgName | |||
+ " is private"); | |||
} | |||
} | |||
boolean popTarget = false; | |||
if ((acc & AccessFlag.STATIC) != 0) { | |||
@@ -530,7 +529,19 @@ public class MemberCodeGen extends CodeGen { | |||
if (op != '=' && !is_static) | |||
bytecode.addOpcode(DUP); | |||
int fi = atFieldRead(f, is_static, op == '='); | |||
int fi; | |||
if (op == '=') { | |||
FieldInfo finfo = f.getFieldInfo2(); | |||
setFieldType(finfo); | |||
AccessorMaker maker = isAccessibleField(f, finfo); | |||
if (maker == null) | |||
fi = addFieldrefInfo(f, finfo); | |||
else | |||
fi = 0; | |||
} | |||
else | |||
fi = atFieldRead(f, is_static); | |||
int fType = exprType; | |||
int fDim = arrayDim; | |||
String cname = className; | |||
@@ -548,21 +559,40 @@ public class MemberCodeGen extends CodeGen { | |||
bytecode.addOpcode(dup_code); | |||
} | |||
if (is_static) { | |||
bytecode.add(PUTSTATIC); | |||
bytecode.growStack(is2w ? -2 : -1); | |||
} | |||
else { | |||
bytecode.add(PUTFIELD); | |||
bytecode.growStack(is2w ? -3 : -2); | |||
} | |||
atFieldAssignCore(f, is_static, fi, is2w); | |||
bytecode.addIndex(fi); | |||
exprType = fType; | |||
arrayDim = fDim; | |||
className = cname; | |||
} | |||
/* If fi == 0, the field must be a private field in an enclosing class. | |||
*/ | |||
private void atFieldAssignCore(CtField f, boolean is_static, int fi, | |||
boolean is2byte) throws CompileError { | |||
if (fi != 0) { | |||
if (is_static) { | |||
bytecode.add(PUTSTATIC); | |||
bytecode.growStack(is2byte ? -2 : -1); | |||
} | |||
else { | |||
bytecode.add(PUTFIELD); | |||
bytecode.growStack(is2byte ? -3 : -2); | |||
} | |||
bytecode.addIndex(fi); | |||
} | |||
else { | |||
CtClass declClass = f.getDeclaringClass(); | |||
AccessorMaker maker = declClass.getAccessorMaker(); | |||
// make should be non null. | |||
FieldInfo finfo = f.getFieldInfo2(); | |||
MethodInfo minfo = maker.getFieldSetter(finfo, is_static); | |||
bytecode.addInvokestatic(declClass, minfo.getName(), | |||
minfo.getDescriptor()); | |||
} | |||
} | |||
/* overwritten in JvstCodeGen. | |||
*/ | |||
public void atMember(Member mem) throws CompileError { | |||
@@ -573,16 +603,73 @@ public class MemberCodeGen extends CodeGen { | |||
{ | |||
CtField f = fieldAccess(expr); | |||
boolean is_static = resultStatic; | |||
atFieldRead(f, is_static, false); | |||
atFieldRead(f, is_static); | |||
} | |||
private int atFieldRead(CtField f, boolean isStatic, boolean noRead) | |||
/** | |||
* Generates bytecode for reading a field value. | |||
* It returns a fieldref_info index or zero if the field is a private | |||
* one declared in an enclosing class. | |||
*/ | |||
private int atFieldRead(CtField f, boolean isStatic) throws CompileError { | |||
FieldInfo finfo = f.getFieldInfo2(); | |||
boolean is2byte = setFieldType(finfo); | |||
AccessorMaker maker = isAccessibleField(f, finfo); | |||
if (maker != null) { | |||
MethodInfo minfo = maker.getFieldGetter(finfo, isStatic); | |||
bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(), | |||
minfo.getDescriptor()); | |||
return 0; | |||
} | |||
else { | |||
int fi = addFieldrefInfo(f, finfo); | |||
if (isStatic) { | |||
bytecode.add(GETSTATIC); | |||
bytecode.growStack(is2byte ? 2 : 1); | |||
} | |||
else { | |||
bytecode.add(GETFIELD); | |||
bytecode.growStack(is2byte ? 1 : 0); | |||
} | |||
bytecode.addIndex(fi); | |||
return fi; | |||
} | |||
} | |||
/** | |||
* Returns null if the field is accessible. Otherwise, it throws | |||
* an exception or it returns AccessorMaker if the field is a private | |||
* one declared in an enclosing class. | |||
*/ | |||
private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo) | |||
throws CompileError | |||
{ | |||
FieldInfo finfo = f.getFieldInfo2(); | |||
String type = finfo.getDescriptor(); | |||
if (AccessFlag.isPrivate(finfo.getAccessFlags()) | |||
&& f.getDeclaringClass() != thisClass) { | |||
CtClass declClass = f.getDeclaringClass(); | |||
if (isEnclosing(declClass, thisClass)) { | |||
AccessorMaker maker = declClass.getAccessorMaker(); | |||
if (maker != null) | |||
return maker; | |||
else | |||
throw new CompileError("fatal error. bug?"); | |||
} | |||
else | |||
throw new CompileError("Field " + f.getName() + " in " | |||
+ declClass.getName() + " is private."); | |||
} | |||
return null; // accessible field | |||
} | |||
int fi = addFieldrefInfo(f, finfo, type); | |||
/** | |||
* Sets exprType, arrayDim, and className. | |||
* | |||
* @return true if the field type is long or double. | |||
*/ | |||
private boolean setFieldType(FieldInfo finfo) throws CompileError { | |||
String type = finfo.getDescriptor(); | |||
int i = 0; | |||
int dim = 0; | |||
@@ -593,7 +680,6 @@ public class MemberCodeGen extends CodeGen { | |||
} | |||
arrayDim = dim; | |||
boolean is2byte = (c == 'J' || c == 'D'); | |||
exprType = MemberResolver.descToType(c); | |||
if (c == 'L') | |||
@@ -601,27 +687,16 @@ public class MemberCodeGen extends CodeGen { | |||
else | |||
className = null; | |||
if (noRead) | |||
return fi; | |||
if (isStatic) { | |||
bytecode.add(GETSTATIC); | |||
bytecode.growStack(is2byte ? 2 : 1); | |||
} | |||
else { | |||
bytecode.add(GETFIELD); | |||
bytecode.growStack(is2byte ? 1 : 0); | |||
} | |||
bytecode.addIndex(fi); | |||
return fi; | |||
boolean is2byte = (c == 'J' || c == 'D'); | |||
return is2byte; | |||
} | |||
protected int addFieldrefInfo(CtField f, FieldInfo finfo, String type) { | |||
private int addFieldrefInfo(CtField f, FieldInfo finfo) { | |||
ConstPool cp = bytecode.getConstPool(); | |||
String cname = f.getDeclaringClass().getName(); | |||
int ci = cp.addClassInfo(cname); | |||
String name = finfo.getName(); | |||
String type = finfo.getDescriptor(); | |||
return cp.addFieldrefInfo(ci, name, type); | |||
} | |||
@@ -634,7 +709,7 @@ public class MemberCodeGen extends CodeGen { | |||
if (!is_static) | |||
bytecode.addOpcode(DUP); | |||
int fi = atFieldRead(f, is_static, false); | |||
int fi = atFieldRead(f, is_static); | |||
int t = exprType; | |||
boolean is2w = is2word(t, arrayDim); | |||
@@ -645,17 +720,7 @@ public class MemberCodeGen extends CodeGen { | |||
dup_code = (is2w ? DUP2_X1 : DUP_X1); | |||
atPlusPlusCore(dup_code, doDup, token, isPost, expr); | |||
if (is_static) { | |||
bytecode.add(PUTSTATIC); | |||
bytecode.growStack(is2w ? -2 : -1); | |||
} | |||
else { | |||
bytecode.add(PUTFIELD); | |||
bytecode.growStack(is2w ? -3 : -2); | |||
} | |||
bytecode.addIndex(fi); | |||
atFieldAssignCore(f, is_static, fi, is2w); | |||
} | |||
/* This method also returns a value in resultStatic. |
@@ -1006,10 +1006,15 @@ public final class Parser implements TokenId { | |||
break; | |||
case '.' : | |||
lex.get(); | |||
if (lex.get() != Identifier) | |||
t = lex.get(); | |||
if (t == CLASS) | |||
str = "class"; | |||
else if (t == Identifier) | |||
str = lex.getString(); | |||
else | |||
throw new CompileError("missing member name", lex); | |||
expr = Expr.make('.', expr, new Member(lex.getString())); | |||
expr = Expr.make('.', expr, new Member(str)); | |||
break; | |||
case '#' : | |||
lex.get(); |
@@ -26,6 +26,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { | |||
static final String javaLangObject = "java.lang.Object"; | |||
static final String jvmJavaLangObject = "java/lang/Object"; | |||
static final String jvmJavaLangString = "java/lang/String"; | |||
static final String jvmJavaLangClass = "java/lang/Class"; | |||
/* The following fields are used by atXXX() methods | |||
* for returning the type of the compiled expression. | |||
@@ -393,13 +394,20 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { | |||
int token = expr.getOperator(); | |||
ASTree oprand = expr.oprand1(); | |||
if (token == '.') | |||
if (((Symbol)expr.oprand2()).get().equals("length")) | |||
if (token == '.') { | |||
String member = ((Symbol)expr.oprand2()).get(); | |||
if (member.equals("length")) | |||
atArrayLength(expr); | |||
else if (member.equals("class")) | |||
atClassObject(expr); // .class | |||
else | |||
atFieldRead(expr); | |||
} | |||
else if (token == MEMBER) { // field read | |||
if (!atClassObject(expr)) // .class | |||
String member = ((Symbol)expr.oprand2()).get(); | |||
if (member.equals("class")) | |||
atClassObject(expr); // .class | |||
else | |||
atFieldRead(expr); | |||
} | |||
else if (token == ARRAY) | |||
@@ -624,14 +632,10 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { | |||
throw new CompileError("bad filed access"); | |||
} | |||
public boolean atClassObject(Expr expr) throws CompileError { | |||
if (!((Symbol)expr.oprand2()).get().equals("class")) | |||
return false; | |||
if (resolveClassName((ASTList)expr.oprand1()) == null) | |||
return false; | |||
return true; | |||
public void atClassObject(Expr expr) throws CompileError { | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className =jvmJavaLangClass; | |||
} | |||
public void atArrayLength(Expr expr) throws CompileError { |
@@ -104,15 +104,24 @@ public class Declarator extends ASTList implements TokenId { | |||
return null; | |||
StringBuffer sbuf = new StringBuffer(); | |||
astToClassName(sbuf, name, sep); | |||
return sbuf.toString(); | |||
} | |||
private static void astToClassName(StringBuffer sbuf, ASTList name, | |||
char sep) { | |||
for (;;) { | |||
sbuf.append(((Symbol)name.head()).get()); | |||
ASTree h = name.head(); | |||
if (h instanceof Symbol) | |||
sbuf.append(((Symbol)h).get()); | |||
else if (h instanceof ASTList) | |||
astToClassName(sbuf, (ASTList)h, sep); | |||
name = name.tail(); | |||
if (name == null) | |||
break; | |||
sbuf.append(sep); | |||
} | |||
return sbuf.toString(); | |||
} | |||
} |