]> source.dussan.org Git - javassist.git/commitdiff
changed the behavior of CtClass#getMethods(), getFields(), and getConstructors().
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Fri, 4 Nov 2005 09:29:35 +0000 (09:29 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Fri, 4 Nov 2005 09:29:35 +0000 (09:29 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@217 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

Readme.html
sample/preproc/Assistant.java [new file with mode: 0644]
sample/preproc/Compiler.java [new file with mode: 0644]
sample/preproc/package.html [new file with mode: 0644]
src/main/javassist/CtClass.java
src/main/javassist/CtClassType.java
src/main/javassist/reflect/ClassMetaobject.java
tutorial/tutorial.html

index 8cbfa2b961d6945dc932d411ae68476c9a1d027c..5ecc3b3b8b4061da9995ba773210eba3bdf3d6a9 100644 (file)
@@ -284,6 +284,9 @@ see javassist.Dump.
 <p>- version 3.1
 
 <ul>
+  <li>getFields(), getMethods(), and getConstructors() in CtClass
+    were changed to return non-private memebers instead of only
+    public members.
   <li>getEnclosingClass() in javassist.CtClass was renamed
       to getEnclosingMethod().
   <li>toMethod() in javassist.CtConstructor has been implemented.
diff --git a/sample/preproc/Assistant.java b/sample/preproc/Assistant.java
new file mode 100644 (file)
index 0000000..81b93b2
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2005 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 sample.preproc;
+
+import javassist.CtClass;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+
+/**
+ * This is an interface for objects invoked by the
+ * Javassist preprocessor when the preprocessor encounters an annotated
+ * import declaration.
+ *
+ * @see sample.preproc.Compiler
+ */
+public interface Assistant {
+    /**
+     * Is called when the Javassist preprocessor encounters an
+     * import declaration annotated with the "by" keyword.
+     *
+     * <p>The original import declaration is replaced with new import
+     * declarations of classes returned by this method.  For example,
+     * the following implementation does not change the original
+     * declaration:
+     *
+     * <ul><pre>
+     * public CtClass[] assist(ClassPool cp, String importname, String[] args) {
+     *     return new CtClass[] { cp.get(importname) };
+     * }
+     * </pre></uL>
+     *
+     * @param cp                class pool
+     * @param importname        the class imported by the declaration
+     * @param args              the parameters specified by the annotation
+     * @return                  the classes imported in the java source
+     *                          program produced by the preprocessor.
+     */
+    public CtClass[] assist(ClassPool cp, String importname,
+                            String[] args) throws CannotCompileException;
+}
diff --git a/sample/preproc/Compiler.java b/sample/preproc/Compiler.java
new file mode 100644 (file)
index 0000000..5481c0f
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2005 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 sample.preproc;
+
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.util.Vector;
+import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.ClassPool;
+
+/**
+ * This is a preprocessor for Java source programs using annotated
+ * import declarations.
+ *
+ * <ul><pre>
+ * import <i>class-name</i> by <i>assistant-name</i> [(<i>arg1, arg2, ...</i>)]
+ * </pre></ul>
+ *
+ * <p>To process this annotation, run this class as follows:
+ *
+ * <ul><pre>
+ * java sample.preproc.Compiler sample.j
+ * </pre></ul>
+ *
+ * <p>This command produces <code>sample.java</code>, which only includes
+ * regular import declarations.  Also, the Javassist program
+ * specified by <i>assistant-name</i> is executed so that it produces
+ * class files under the <code>./tmpjvst</code> directory.  The class
+ * specified by <i>assistant-name</i> must implement
+ * <code>sample.preproc.Assistant</code>.
+ *
+ * @see sample.preproc.Assistant
+ */
+
+public class Compiler {
+    protected BufferedReader input;
+    protected BufferedWriter output;
+    protected ClassPool classPool;
+
+    /**
+     * Constructs a <code>Compiler</code> with a source file.
+     *
+     * @param inputname         the name of the source file.
+     */
+    public Compiler(String inputname) throws CannotCompileException {
+        try {
+            input = new BufferedReader(new FileReader(inputname));
+        }
+        catch (IOException e) {
+            throw new CannotCompileException("cannot open: " + inputname);
+        }
+
+        String outputname = getOutputFilename(inputname);
+        if (outputname.equals(inputname))
+            throw new CannotCompileException("invalid source name: "
+                                             + inputname);
+
+        try {
+            output = new BufferedWriter(new FileWriter(outputname));
+        }
+        catch (IOException e) {
+            throw new CannotCompileException("cannot open: " + outputname);
+        }
+
+        classPool = ClassPool.getDefault();
+    }
+
+    /**
+     * Starts preprocessing.
+     */
+    public void process() throws IOException, CannotCompileException {
+        int c;
+        CommentSkipper reader = new CommentSkipper(input, output);
+        while ((c = reader.read()) != -1) {
+            output.write(c);
+            if (c == 'p') {
+                if (skipPackage(reader))
+                    break;
+            }
+            else if (c == 'i')
+                readImport(reader);
+            else if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
+                break;
+        }
+
+        while ((c = input.read()) != -1)
+            output.write(c);
+
+        input.close();
+        output.close();
+    }
+
+    private boolean skipPackage(CommentSkipper reader) throws IOException {
+        int c;
+        c = reader.read();
+        output.write(c);
+        if (c != 'a')
+            return true;
+
+        while ((c = reader.read()) != -1) {
+            output.write(c);
+            if (c == ';')
+                break;
+        }
+
+        return false;
+    }
+
+    private void readImport(CommentSkipper reader)
+                                throws IOException, CannotCompileException
+    {
+        int word[] = new int[5];
+        int c;
+        for (int i = 0; i < 5; ++i) {
+            word[i] = reader.read();
+            output.write(word[i]);
+        }
+
+        if (word[0] != 'm' || word[1] != 'p' || word[2] != 'o'
+            || word[3] != 'r' || word[4] != 't')
+            return;     // syntax error?
+
+        c = skipSpaces(reader, ' ');
+        StringBuffer classbuf = new StringBuffer();
+        while (c != ' ' && c != '\t' && c != '\n' && c != '\r'
+               && c != ';' && c != -1) {
+            classbuf.append((char)c);
+            c = reader.read();
+        }
+
+        String importclass = classbuf.toString();
+        c = skipSpaces(reader, c);
+        if (c == ';') {
+            output.write(importclass);
+            output.write(';');
+            return;
+        }
+        if (c != 'b')
+            syntaxError(importclass);
+
+        reader.read();  // skip 'y'
+
+        StringBuffer assistant = new StringBuffer();
+        Vector args = new Vector();
+        c = readAssistant(reader, importclass, assistant, args);
+        c = skipSpaces(reader, c);
+        if (c != ';')
+            syntaxError(importclass);
+
+        runAssistant(importclass, assistant.toString(), args);
+    }
+
+    void syntaxError(String importclass) throws CannotCompileException {
+        throw new CannotCompileException("Syntax error.  Cannot import "
+                                         + importclass);
+    }
+
+    int readAssistant(CommentSkipper reader, String importclass,
+                      StringBuffer assistant, Vector args)
+        throws IOException, CannotCompileException
+    {
+        int c = readArgument(reader, assistant);
+        c = skipSpaces(reader, c);
+        if (c == '(') {
+            do {
+                StringBuffer arg = new StringBuffer();
+                c = readArgument(reader, arg);
+                args.addElement(arg.toString());
+                c = skipSpaces(reader, c);
+            } while (c == ',');
+
+            if (c != ')')
+                syntaxError(importclass);
+
+            return reader.read();
+        }
+
+        return c;
+    }
+
+    int readArgument(CommentSkipper reader, StringBuffer buf)
+        throws IOException
+    {
+        int c = skipSpaces(reader, ' ');
+        while ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
+               || '0' <= c && c <= '9' || c == '.' || c == '_') {
+            buf.append((char)c);
+            c = reader.read();
+        }
+
+        return c;
+    }
+
+    int skipSpaces(CommentSkipper reader, int c) throws IOException {
+        while (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
+            if (c == '\n' || c == '\r')
+                output.write(c);
+
+            c = reader.read();
+        }
+
+        return c;
+    }
+
+    /**
+     * Is invoked if this compiler encoutenrs:
+     *
+     * <ul><pre>
+     * import <i>class name</i> by <i>assistant</i> (<i>args1</i>, <i>args2</i>, ...);
+     * </pre></ul>
+     *
+     * @param   classname       class name
+     * @param   assistantname   assistant
+     * @param   argv            args1, args2, ...
+     */
+    private void runAssistant(String importname, String assistantname,
+                              Vector argv)
+        throws IOException, CannotCompileException
+    {
+        Class assistant;
+        Assistant a;
+        int s = argv.size();
+        String[] args = new String[s];
+        for (int i = 0; i < s; ++i)
+            args[i] = (String)argv.elementAt(i);
+
+        try {
+            assistant = Class.forName(assistantname);
+        }
+        catch (ClassNotFoundException e) {
+            throw new CannotCompileException("Cannot find " + assistantname);
+        }
+
+        try {
+            a = (Assistant)assistant.newInstance();
+        }
+        catch (Exception e) {
+            throw new CannotCompileException(e);
+        }
+
+        CtClass[] imports = a.assist(classPool, importname, args);
+        s = imports.length;
+        if (s < 1)
+            output.write(" java.lang.Object;");
+        else {
+            output.write(' ');
+            output.write(imports[0].getName());
+            output.write(';');
+            for (int i = 1; i < s; ++i) {
+                output.write(" import ");
+                output.write(imports[1].getName());
+                output.write(';');
+            }
+        }
+    }
+
+    private String getOutputFilename(String input) {
+        int i = input.lastIndexOf('.');
+        if (i < 0)
+            i = input.length();
+
+        return input.substring(0, i) + ".java";
+    }
+
+    public static void main(String[] args) {
+        if (args.length > 0)
+            try {
+                Compiler c = new Compiler(args[0]);
+                c.process();
+            }
+            catch (IOException e) {
+                System.err.println(e);
+            }
+            catch (CannotCompileException e) {
+                System.err.println(e);
+            }
+        else {
+            System.err.println("Javassist version " + CtClass.version);
+            System.err.println("No source file is specified.");
+        }
+    }
+}
+
+class CommentSkipper {
+    private BufferedReader input;
+    private BufferedWriter output;
+
+    public CommentSkipper(BufferedReader reader, BufferedWriter writer) {
+        input = reader;
+        output = writer;
+    }
+
+    public int read() throws IOException {
+        int c;
+        while ((c = input.read()) != -1)
+            if (c != '/')
+                return c;
+            else {
+                c = input.read();
+                if (c == '/')
+                    skipCxxComments();
+                else if (c == '*')
+                    skipCComments();
+                else 
+                    output.write('/');
+            }
+
+        return c;
+    }
+
+    private void skipCxxComments() throws IOException {
+        int c;
+        output.write("//");
+        while ((c = input.read()) != -1) {
+            output.write(c);
+            if (c == '\n' || c == '\r')
+                break;
+        }
+    }
+
+    private void skipCComments() throws IOException {
+        int c;
+        boolean star = false;
+        output.write("/*");
+        while ((c = input.read()) != -1) {
+            output.write(c);
+            if (c == '*')
+                star = true;
+            else if(star && c == '/')
+                break;
+            else
+                star = false;
+        }
+    }
+}
diff --git a/sample/preproc/package.html b/sample/preproc/package.html
new file mode 100644 (file)
index 0000000..dee15e0
--- /dev/null
@@ -0,0 +1,14 @@
+<html>
+<body>
+A sample preprocessor.
+
+<p>The preprocessor for running Javassist at compile time.
+The produced class files are saved on a local disk.
+
+<p>This package is provided as a sample implementation of the
+preprocessor using Javassist.  All the programs in this package
+uses only the regular Javassist API; they never call any hidden
+methods.
+
+</body>
+</html>
index c0962808729c0240d5ac22078b7bd816c10255ef..55f81b8127beacfe8927043cf7338f7bea607541 100644 (file)
@@ -582,8 +582,8 @@ public abstract class CtClass {
 
     /**
      * Returns an array containing <code>CtField</code> objects
-     * representing all the public fields of the class.
-     * That array includes public fields inherited from the
+     * representing all the non-private fields of the class.
+     * That array includes non-private fields inherited from the
      * superclasses.
      */
     public CtField[] getFields() { return new CtField[0]; }
@@ -628,7 +628,7 @@ public abstract class CtClass {
 
     /**
      * Returns an array containing <code>CtConstructor</code> objects
-     * representing all the public constructors of the class.
+     * representing all the non-private constructors of the class.
      */
     public CtConstructor[] getConstructors() {
         return new CtConstructor[0];
@@ -686,8 +686,8 @@ public abstract class CtClass {
 
     /**
      * Returns an array containing <code>CtMethod</code> objects
-     * representing all the public methods of the class.
-     * That array includes public methods inherited from the
+     * representing all the non-private methods of the class.
+     * That array includes pon-private methods inherited from the
      * superclasses.
      */
     public CtMethod[] getMethods() {
@@ -1043,12 +1043,13 @@ public abstract class CtClass {
      * is on by default.  Otherwise, it is off.
      * 
      * @param stop      disallow pruning if true.  Otherwise, allow.
+     * @return the previous status of pruning.  true if pruning is already stopped.
      *
      * @see #detach()
      * @see #prune()
      * @see ClassPool#doPruning
      */
-    public void stopPruning(boolean stop) {}
+    public boolean stopPruning(boolean stop) { return true; }
 
     /**
      * Discards unnecessary attributes, in particuar,
@@ -1144,6 +1145,25 @@ public abstract class CtClass {
         }
     }
 
+    /**
+     * Writes a class file as <code>writeFile()</code> does although this
+     * method does not prune or freeze the class after writing the class
+     * file.  Note that, once <code>writeFile()</code> or <code>toBytecode()</code>
+     * is called, it cannot be called again since the class is pruned and frozen.
+     * This method would be useful for debugging.
+     */
+    public void debugWriteFile() {
+        try {
+            boolean p = stopPruning(true);
+            writeFile();
+            defrost();
+            stopPruning(p);
+        }
+        catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     static class DelayedFileOutputStream extends OutputStream {
         private FileOutputStream file;
         private String filename;
index 8d8e67d1abfc9dd6b5388a3bbc2bcfe38e1d10e0..46fb268a98887dd332fc3b38146c4f7e4e785e95 100644 (file)
@@ -564,7 +564,7 @@ class CtClassType extends CtClass {
 
         CtMember cf = ((CtClassType)cc).getFieldsCache();
         while (cf != null) {
-            if (Modifier.isPublic(cf.getModifiers()))
+            if (!Modifier.isPrivate(cf.getModifiers()))
                 alist.add(cf);
 
             cf = cf.next;
@@ -680,7 +680,7 @@ class CtClassType extends CtClass {
         int n = 0;
         int i = cons.length;
         while (--i >= 0)
-            if (Modifier.isPublic(cons[i].getModifiers()))
+            if (!Modifier.isPrivate(cons[i].getModifiers()))
                 ++n;
 
         CtConstructor[] result = new CtConstructor[n];
@@ -688,7 +688,7 @@ class CtClassType extends CtClass {
         i = cons.length;
         while (--i >= 0) {
             CtConstructor c = cons[i];
-            if (Modifier.isPublic(c.getModifiers()))
+            if (!Modifier.isPrivate(c.getModifiers()))
                 result[n++] = c;
         }
 
@@ -756,7 +756,7 @@ class CtClassType extends CtClass {
     public CtMethod[] getMethods() {
         HashMap h = new HashMap();
         getMethods0(h, this);
-        return (CtMethod[])h.values().toArray(new CtMethod[0]);
+        return (CtMethod[])h.values().toArray(new CtMethod[h.size()]);
     }
 
     private static void getMethods0(HashMap h, CtClass cc) {
@@ -778,7 +778,7 @@ class CtClassType extends CtClass {
         if (cc instanceof CtClassType) {
             CtMember cm = ((CtClassType)cc).getMethodsCache();
             while (cm != null) {
-                if (Modifier.isPublic(cm.getModifiers()))
+                if (!Modifier.isPrivate(cm.getModifiers()))
                     h.put(((CtMethod)cm).getStringRep(), cm);
 
                 cm = cm.next;
@@ -1120,8 +1120,10 @@ class CtClassType extends CtClass {
                                        + " was pruned.");
     }
 
-    public void stopPruning(boolean stop) {
+    public boolean stopPruning(boolean stop) {
+        boolean prev = !doPruning;
         doPruning = !stop;
+        return prev;
     }
 
     private void modifyClassConstructor(ClassFile cf)
index bfbc3f4844b85574844c0f8273c370bd916b167a..61328452b09ab91473b2ef63e3aacfbee2067318 100644 (file)
@@ -251,7 +251,8 @@ public class ClassMetaobject implements Serializable {
         Class baseclass = getJavaClass();
         Method[] allmethods = baseclass.getDeclaredMethods();
         int n = allmethods.length;
-        methods = new Method[n];
+        int[] index = new int[n];
+        int max = 0;
         for (int i = 0; i < n; ++i) {
             Method m = allmethods[i];
             String mname = m.getName();
@@ -265,10 +266,17 @@ public class ClassMetaobject implements Serializable {
                         break;
                 }
 
-                methods[k] = m;
+                index[i] = ++k;
+                if (k > max)
+                    max = k;
             }
         }
 
+        methods = new Method[max];
+        for (int i = 0; i < n; ++i)
+            if (index[i] > 0)
+                methods[index[i] - 1] = allmethods[i];
+
         return methods;
     }
 
index 271e88de5260d82107bb120a66e33b8fbba3bf0f..9644016ad26685a58bab9fd620b80efb02412852 100644 (file)
@@ -177,6 +177,13 @@ cc.setSuperclass(...);    // OK since the class is not frozen.
 <p>To disallow pruning for all the <code>CtClass</code>es, set
 <code>ClassPool.doPruning</code> to <code>false</code>.
 
+<ul><b>Note:</b>
+While debugging, you might want to temporarily stop pruning and write a modified
+class file to a disk drive.  <code>debugWriteFile()</code> is a convenient method
+for that purpose.  It stops pruning, write a class file, defrost it, and turn
+pruning on again (if it was initially on).
+</ul>
+
 <h4>Class search path</h4>
 
 <p>The default <code>ClassPool</code> returned