diff options
-rw-r--r-- | Readme.html | 3 | ||||
-rw-r--r-- | sample/preproc/Assistant.java | 53 | ||||
-rw-r--r-- | sample/preproc/Compiler.java | 352 | ||||
-rw-r--r-- | sample/preproc/package.html | 14 | ||||
-rw-r--r-- | src/main/javassist/CtClass.java | 32 | ||||
-rw-r--r-- | src/main/javassist/CtClassType.java | 14 | ||||
-rw-r--r-- | src/main/javassist/reflect/ClassMetaobject.java | 12 | ||||
-rw-r--r-- | tutorial/tutorial.html | 7 |
8 files changed, 473 insertions, 14 deletions
diff --git a/Readme.html b/Readme.html index 8cbfa2b9..5ecc3b3b 100644 --- a/Readme.html +++ b/Readme.html @@ -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 index 00000000..81b93b25 --- /dev/null +++ b/sample/preproc/Assistant.java @@ -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 index 00000000..5481c0fc --- /dev/null +++ b/sample/preproc/Compiler.java @@ -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 index 00000000..dee15e02 --- /dev/null +++ b/sample/preproc/package.html @@ -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> diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index c0962808..55f81b81 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -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; diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 8d8e67d1..46fb268a 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -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) diff --git a/src/main/javassist/reflect/ClassMetaobject.java b/src/main/javassist/reflect/ClassMetaobject.java index bfbc3f48..61328452 100644 --- a/src/main/javassist/reflect/ClassMetaobject.java +++ b/src/main/javassist/reflect/ClassMetaobject.java @@ -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; } diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html index 271e88de..9644016a 100644 --- a/tutorial/tutorial.html +++ b/tutorial/tutorial.html @@ -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 |