]> source.dussan.org Git - javassist.git/commitdiff
Now, the compiler accepts a method that calls a private method
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Tue, 4 May 2004 17:22:40 +0000 (17:22 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Tue, 4 May 2004 17:22:40 +0000 (17:22 +0000)
declared in an enclosing class.

git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@97 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

src/main/javassist/CtClass.java
src/main/javassist/CtClassType.java
src/main/javassist/bytecode/Bytecode.java
src/main/javassist/bytecode/ExceptionsAttribute.java
src/main/javassist/compiler/AccessorMaker.java [new file with mode: 0644]
src/main/javassist/compiler/CompileError.java
src/main/javassist/compiler/MemberCodeGen.java

index f3e8963e474bc3b93035be892de081e34e0ff337..74f953be369134f9a0efd8ca8a4ff332c64c77a1 100644 (file)
@@ -203,6 +203,13 @@ public abstract class CtClass {
      */
     public ClassFile getClassFile2() { return null; }
 
+    /**
+     * Undocumented method.  Do not use; internal-use only.
+     */
+    public javassist.compiler.AccessorMaker getAccessorMaker() {
+        return null;
+    }
+
     /**
      * Returns the uniform resource locator (URL) of the class file.
      */
@@ -227,7 +234,7 @@ public abstract class CtClass {
 
     void checkModify() throws RuntimeException {
         if (isFrozen())
-            throw new RuntimeException("the class is frozen");
+            throw new RuntimeException(getName() + " class is frozen");
 
         // isModified() must return true after this method is invoked.
     }
index e503abcb40b4bb18715a86a396b627f9201e4d75..de88c855c654e7136b64d619c8c404ffc01a9b32 100644 (file)
@@ -18,6 +18,7 @@ package javassist;
 import javassist.bytecode.*;
 import javassist.compiler.Javac;
 import javassist.compiler.CompileError;
+import javassist.compiler.AccessorMaker;
 import javassist.expr.ExprEditor;
 import java.io.InputStream;
 import java.io.BufferedInputStream;
@@ -44,6 +45,8 @@ class CtClassType extends CtClass {
     private CtConstructor classInitializerCache;
     private CtMethod methodsCache;
 
+    private AccessorMaker accessors;
+
     private FieldInitLink fieldInitializers;
     private Hashtable hiddenMethods;    // must be synchronous
     private int uniqueNumberSeed;
@@ -53,6 +56,7 @@ class CtClassType extends CtClass {
         classPool = cp;
         wasChanged = wasFrozen = false;
         classfile = null;
+        accessors = null;
         fieldInitializers = null;
         hiddenMethods = null;
         uniqueNumberSeed = 0;
@@ -104,6 +108,13 @@ class CtClassType extends CtClass {
         methodsCache = null;
     }
 
+    public AccessorMaker getAccessorMaker() {
+        if (accessors == null)
+            accessors = new AccessorMaker(this);
+
+        return accessors;
+    }
+
     public ClassFile getClassFile2() {
         if (classfile != null)
             return classfile;
index 0aa966492aec163a1550387c577ffa75077f7ff2..ae6f1691b8e48ba8674b9e27c1810285d72ae30a 100644 (file)
@@ -78,6 +78,19 @@ public class Bytecode implements Opcode {
         stackDepth = 0;
     }
 
+    /**
+     * Constructs a <code>Bytecode</code> object with an empty bytecode
+     * sequence.  The initial values of <code>max_stack</code> and
+     * <code>max_locals</code> are zero.
+     * 
+     * @param cp            constant pool table.
+     * @see Bytecode#setMaxStack(int)
+     * @see Bytecode#setMaxLocals(int)
+     */
+    public Bytecode(ConstPool cp) {
+        this(cp, 0, 0);
+    }
+
     /* used in add().
      */
     private Bytecode() {
index 3f06376b32e6588bb873f05e89b734ddc4973030..0a1034528a75fc87ba2855909b31f0da800f4646 100644 (file)
@@ -64,7 +64,7 @@ public class ExceptionsAttribute extends AttributeInfo {
      *
      * @param newCp     the constant pool table used by the new copy.
      * @param classnames        pairs of replaced and substituted
-     *                          class names.
+     *                          class names.  It can be <code>null</code>.
      */
     public AttributeInfo copy(ConstPool newCp, Map classnames) {
         return new ExceptionsAttribute(newCp, this, classnames);
diff --git a/src/main/javassist/compiler/AccessorMaker.java b/src/main/javassist/compiler/AccessorMaker.java
new file mode 100644 (file)
index 0000000..f353a40
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2004 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.
+ *
+ * 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.compiler;
+
+import javassist.*;
+import javassist.bytecode.*;
+import java.util.HashMap;
+
+/**
+ * AccessorMaker maintains accessors to private members of an enclosing
+ * class.  It is necessary for compiling a method in an inner class.
+ */
+public class AccessorMaker {
+    private CtClass clazz;
+    private int uniqueNumber;
+    private HashMap accessors;
+
+    public AccessorMaker(CtClass c) {
+        clazz = c;
+        uniqueNumber = 1;
+        accessors = new HashMap();
+    }
+
+    /**
+     * Returns the name of the method for accessing a private method.
+     *
+     * @param name      the name of the private method.
+     * @param desc      the descriptor of the private method.
+     * @param accDesc   the descriptor of the accessor method.  The first
+     *                  parameter type is <code>clazz</code>.
+     *                  If the private method is static,
+     *              <code>accDesc<code> must be equal to <code>desc</code>. 
+     *                  
+     * @param orig      the method info of the private method.
+     * @return
+     */
+    public String getMethodAccessor(String name, String desc, String accDesc,
+                                    MethodInfo orig)
+        throws CompileError
+    {
+        String key = name + ":" + desc;
+        String accName = (String)accessors.get(key);
+        if (accName != null)
+            return accName;     // already exists.
+
+        ClassFile cf = clazz.getClassFile();    // turn on the modified flag. 
+        do {
+            accName = "access$" + uniqueNumber++;
+        } while (cf.getMethod(accName) != null);
+
+        try {
+            ConstPool cp = cf.getConstPool();
+            ClassPool pool = clazz.getClassPool();
+            MethodInfo minfo
+                = new MethodInfo(cp, accName, accDesc);
+            minfo.setAccessFlags(AccessFlag.STATIC);
+            minfo.addAttribute(new SyntheticAttribute(cp));
+            ExceptionsAttribute ea = orig.getExceptionsAttribute();
+            if (ea != null)  
+                minfo.addAttribute(ea.copy(cp, null));
+
+            CtClass[] params = Descriptor.getParameterTypes(accDesc, pool);
+            int regno = 0;
+            Bytecode code = new Bytecode(cp);
+            for (int i = 0; i < params.length; ++i)
+                regno += code.addLoad(regno, params[i]);
+
+            code.setMaxLocals(regno);
+            if (desc == accDesc)
+                code.addInvokestatic(clazz, name, desc);
+            else
+                code.addInvokevirtual(clazz, name, desc);
+
+            code.addReturn(Descriptor.getReturnType(desc, pool));
+            minfo.setCodeAttribute(code.toCodeAttribute());
+            cf.addMethod(minfo);
+        }
+        catch (CannotCompileException e) {
+            throw new CompileError(e);
+        }
+        catch (NotFoundException e) {
+            throw new CompileError(e);
+        }
+
+        accessors.put(key, accName);
+        return accName;
+    }
+}
index 01c51a4331e7b5819650bb5df87d39693f38176b..5c877da8dfa085f01efa0e9b500947f3577df498 100644 (file)
@@ -15,6 +15,9 @@
 
 package javassist.compiler;
 
+import javassist.CannotCompileException;
+import javassist.NotFoundException;
+
 public class CompileError extends Exception {
     private Lex lex;
     private String reason;
@@ -29,6 +32,14 @@ public class CompileError extends Exception {
         lex = null;
     }
 
+    public CompileError(CannotCompileException e) {
+        this(e.getReason());
+    }
+
+    public CompileError(NotFoundException e) {
+        this("cannot find " + e.getMessage());
+    }
+
     public String getMessage() {
         return reason;
     }
index e8c5ba3b8d636d5abfc18dbd87923e1c029f1e97..549626db427c35d0dde4d9ff30a14dd61da1e235 100644 (file)
@@ -351,6 +351,16 @@ public class MemberCodeGen extends CodeGen {
             throw new CompileError(msg);
         }
 
+        atMethodCallCore2(targetClass, mname, isStatic, isSpecial,
+                          aload0pos, count, found);
+    }
+
+    private void atMethodCallCore2(CtClass targetClass, String mname,
+                                   boolean isStatic, boolean isSpecial,
+                                   int aload0pos, int count,
+                                   MemberResolver.Method found)
+        throws CompileError
+    {
         CtClass declClass = found.declaring;
         MethodInfo minfo = found.info;
         String desc = minfo.getDescriptor();
@@ -362,11 +372,24 @@ public class MemberCodeGen extends CodeGen {
                 throw new CompileError("no such a constructor");
         }
         else if ((acc & AccessFlag.PRIVATE) != 0) {
-            isSpecial = true;
-            String orgName = mname;
-            mname = getAccessiblePrivate(mname, declClass);
-            if (mname == null)
-                throw new CompileError("Method " + orgName + " is private");
+            if (declClass == thisClass)
+                isSpecial = true;
+            else {
+                String orgName = mname;
+                isSpecial = false;
+                isStatic = true;
+                String origDesc = desc;
+                if ((acc & AccessFlag.STATIC) == 0)
+                    desc = Descriptor.insertParameter(declClass.getName(),
+                                                      origDesc);
+
+                acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC;
+                mname = getAccessiblePrivate(mname, origDesc, desc,
+                                             minfo, declClass);
+                if (mname == null)
+                    throw new CompileError("Method " + orgName
+                                           + " is private");
+            }
         }
 
         boolean popTarget = false;
@@ -386,7 +409,7 @@ public class MemberCodeGen extends CodeGen {
 
             bytecode.addInvokestatic(declClass, mname, desc);
         }
-        else if (isSpecial)
+        else if (isSpecial)    // if (isSpecial && notStatic(acc))
             bytecode.addInvokespecial(declClass, mname, desc);
         else if (declClass.isInterface())
             bytecode.addInvokeinterface(declClass, mname, desc, count);
@@ -399,12 +422,26 @@ public class MemberCodeGen extends CodeGen {
         setReturnType(desc, isStatic, popTarget);
     }
 
-    protected String getAccessiblePrivate(String methodName,
-                                          CtClass declClass) {
-        if (declClass == thisClass)
-            return methodName;
-        else if (isEnclosing(declClass, thisClass))
-            return null;
+    /*
+     * Finds (or adds if necessary) a hidden accessor if the method
+     * is in an enclosing class.
+     *
+     * @param desc          the descriptor of the method.
+     * @param declClass     the class declaring the method.
+     */
+    protected String getAccessiblePrivate(String methodName, String desc,
+                                          String newDesc, MethodInfo minfo,
+                                          CtClass declClass)
+        throws CompileError
+    {
+        if (isEnclosing(declClass, thisClass)) {
+            AccessorMaker maker = declClass.getAccessorMaker();
+            if (maker == null)
+                return null;
+            else
+                return maker.getMethodAccessor(methodName, desc, newDesc,
+                                               minfo);
+        }
         else
             return null;    // cannot access this private method.
     }