From d906bfb3374b900b287ae12a1a837e0ffd9c21c5 Mon Sep 17 00:00:00 2001 From: chiba Date: Wed, 5 May 2004 17:17:45 +0000 Subject: [PATCH] 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 --- src/main/javassist/CtClass.java | 2 +- src/main/javassist/bytecode/AccessFlag.java | 29 ++++ src/main/javassist/bytecode/ClassFile.java | 11 +- .../javassist/bytecode/ClassFileWriter.java | 1 + .../javassist/compiler/AccessorMaker.java | 119 +++++++++++++- src/main/javassist/compiler/CodeGen.java | 29 ++-- .../javassist/compiler/MemberCodeGen.java | 155 +++++++++++++----- src/main/javassist/compiler/Parser.java | 9 +- src/main/javassist/compiler/TypeChecker.java | 26 +-- .../javassist/compiler/ast/Declarator.java | 15 +- 10 files changed, 316 insertions(+), 80 deletions(-) diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 74f953be..8641f970 100644 --- a/src/main/javassist/CtClass.java +++ b/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. diff --git a/src/main/javassist/bytecode/AccessFlag.java b/src/main/javassist/bytecode/AccessFlag.java index 7ee1125b..79e75cdf 100644 --- a/src/main/javassist/bytecode/AccessFlag.java +++ b/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 accflags. */ diff --git a/src/main/javassist/bytecode/ClassFile.java b/src/main/javassist/bytecode/ClassFile.java index 65e1392e..ab8e3799 100644 --- a/src/main/javassist/bytecode/ClassFile.java +++ b/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); diff --git a/src/main/javassist/bytecode/ClassFileWriter.java b/src/main/javassist/bytecode/ClassFileWriter.java index 95b70f06..9bf44e94 100644 --- a/src/main/javassist/bytecode/ClassFileWriter.java +++ b/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()); diff --git a/src/main/javassist/compiler/AccessorMaker.java b/src/main/javassist/compiler/AccessorMaker.java index f353a408..bfe59f51 100644 --- a/src/main/javassist/compiler/AccessorMaker.java +++ b/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; + } } diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java index 5b28c7d8..5878d2b6 100644 --- a/src/main/javassist/compiler/CodeGen.java +++ b/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 { diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index 549626db..0a58dc97 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/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. diff --git a/src/main/javassist/compiler/Parser.java b/src/main/javassist/compiler/Parser.java index fedf7bec..62de6bcc 100644 --- a/src/main/javassist/compiler/Parser.java +++ b/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(); diff --git a/src/main/javassist/compiler/TypeChecker.java b/src/main/javassist/compiler/TypeChecker.java index df60efc4..196c7642 100644 --- a/src/main/javassist/compiler/TypeChecker.java +++ b/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 { diff --git a/src/main/javassist/compiler/ast/Declarator.java b/src/main/javassist/compiler/ast/Declarator.java index 9d6bd39b..7571115d 100644 --- a/src/main/javassist/compiler/ast/Declarator.java +++ b/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(); } } -- 2.39.5