]> source.dussan.org Git - javassist.git/commitdiff
Now, the compiler accepts a method that accesses
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Wed, 5 May 2004 17:17:45 +0000 (17:17 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Wed, 5 May 2004 17:17:45 +0000 (17:17 +0000)
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
src/main/javassist/bytecode/AccessFlag.java
src/main/javassist/bytecode/ClassFile.java
src/main/javassist/bytecode/ClassFileWriter.java
src/main/javassist/compiler/AccessorMaker.java
src/main/javassist/compiler/CodeGen.java
src/main/javassist/compiler/MemberCodeGen.java
src/main/javassist/compiler/Parser.java
src/main/javassist/compiler/TypeChecker.java
src/main/javassist/compiler/ast/Declarator.java

index 74f953be369134f9a0efd8ca8a4ff332c64c77a1..8641f9707e43b54b019998a0e1032716a61c5b54 100644 (file)
@@ -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.
index 7ee1125b97213515e24441724e3ab3399fea9323..79e75cdf04ec696df9dd50cf9f1ad13a522b4bd5 100644 (file)
@@ -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>.
      */
index 65e1392e91a939b566a00ed1d54184e88416aaec..ab8e3799ac68cc459f20a2cb2eb020408f7587be 100644 (file)
@@ -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);
index 95b70f067291447028593922b86bae7dd5c01e9d..9bf44e94fa187256eaf2a37983d2a416cb0d8706 100644 (file)
@@ -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());
 
index f353a408bf4bc2743314422e57b00eccf88680be..bfe59f510630ea57cdac2955b35fda036c751aa1 100644 (file)
@@ -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;
+    }
 }
index 5b28c7d87cc871d455ce8366d04a699669e0f93b..5878d2b63a640ae8f456a0ece918b1eacff78d8b 100644 (file)
@@ -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 {
index 549626db427c35d0dde4d9ff30a14dd61da1e235..0a58dc9711b3416081338fbeaffb111fe84fbff8 100644 (file)
@@ -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.
index fedf7bec812447dd253df9d1e416d551c9289e96..62de6bccd33d47f92d1f3fc9c9cc8fb4a4fdcb9e 100644 (file)
@@ -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();
index df60efc4956fb5a5c35dfef8413fcd3a0a27fb44..196c7642e6e802cf943feebb1e38899cd350b092 100644 (file)
@@ -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 {
index 9d6bd39b6657bf9ab48501c2e13f17c1de9e8eb4..7571115dc78dc09b02378b2d0ca6df463680252a 100644 (file)
@@ -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();
     }
 }