瀏覽代碼

Now, the compiler accepts a method that accesses

a private field declared in an enclosing class.


git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@98 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
tags/rel_3_17_1_ga
chiba 20 年之前
父節點
當前提交
d906bfb337

+ 1
- 1
src/main/javassist/CtClass.java 查看文件

@@ -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.

+ 29
- 0
src/main/javassist/bytecode/AccessFlag.java 查看文件

@@ -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>.
*/

+ 7
- 4
src/main/javassist/bytecode/ClassFile.java 查看文件

@@ -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);

+ 1
- 0
src/main/javassist/bytecode/ClassFileWriter.java 查看文件

@@ -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());


+ 115
- 4
src/main/javassist/compiler/AccessorMaker.java 查看文件

@@ -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;
}
}

+ 19
- 10
src/main/javassist/compiler/CodeGen.java 查看文件

@@ -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 {

+ 110
- 45
src/main/javassist/compiler/MemberCodeGen.java 查看文件

@@ -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.

+ 7
- 2
src/main/javassist/compiler/Parser.java 查看文件

@@ -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();

+ 15
- 11
src/main/javassist/compiler/TypeChecker.java 查看文件

@@ -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 {

+ 12
- 3
src/main/javassist/compiler/ast/Declarator.java 查看文件

@@ -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();
}
}

Loading…
取消
儲存