]> source.dussan.org Git - javassist.git/commitdiff
This commit was generated by cvs2svn to compensate for changes in r2, which
authorpatriot1burke <patriot1burke@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Tue, 22 Apr 2003 13:47:06 +0000 (13:47 +0000)
committerpatriot1burke <patriot1burke@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Tue, 22 Apr 2003 13:47:06 +0000 (13:47 +0000)
included commits to RCS files with non-trunk default branches.

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

166 files changed:
License.html [new file with mode: 0644]
Readme.html [new file with mode: 0644]
build.xml [new file with mode: 0644]
sample/Test.java [new file with mode: 0644]
sample/duplicate/Ball.java [new file with mode: 0644]
sample/duplicate/DuplicatedObject.java [new file with mode: 0644]
sample/duplicate/Main.java [new file with mode: 0644]
sample/duplicate/Viewer.java [new file with mode: 0644]
sample/evolve/CannotCreateException.java [new file with mode: 0644]
sample/evolve/CannotUpdateException.java [new file with mode: 0644]
sample/evolve/DemoLoader.java [new file with mode: 0644]
sample/evolve/DemoServer.java [new file with mode: 0644]
sample/evolve/Evolution.java [new file with mode: 0644]
sample/evolve/Sample.java [new file with mode: 0644]
sample/evolve/VersionManager.java [new file with mode: 0644]
sample/evolve/WebPage.class.0 [new file with mode: 0644]
sample/evolve/WebPage.class.1 [new file with mode: 0644]
sample/evolve/WebPage.java [new file with mode: 0644]
sample/evolve/demo.html [new file with mode: 0644]
sample/evolve/start.html [new file with mode: 0644]
sample/evolve/update.html [new file with mode: 0644]
sample/reflect/Main.java [new file with mode: 0644]
sample/reflect/Person.java [new file with mode: 0644]
sample/reflect/VerboseMetaobj.java [new file with mode: 0644]
sample/rmi/AlertDialog.java [new file with mode: 0644]
sample/rmi/CountApplet.java [new file with mode: 0644]
sample/rmi/Counter.java [new file with mode: 0644]
sample/rmi/inside.gif [new file with mode: 0644]
sample/rmi/start.html [new file with mode: 0644]
sample/rmi/webdemo.html [new file with mode: 0644]
sample/vector/Sample.java [new file with mode: 0644]
sample/vector/Sample2.java [new file with mode: 0644]
sample/vector/Test.j [new file with mode: 0644]
sample/vector/VectorAssistant.java [new file with mode: 0644]
src/main/META-INF/MANIFEST.MF [new file with mode: 0644]
src/main/javassist/ByteArrayClassPath.java [new file with mode: 0644]
src/main/javassist/CannotCompileException.java [new file with mode: 0644]
src/main/javassist/ClassMap.java [new file with mode: 0644]
src/main/javassist/ClassPath.java [new file with mode: 0644]
src/main/javassist/ClassPool.java [new file with mode: 0644]
src/main/javassist/ClassPoolTail.java [new file with mode: 0644]
src/main/javassist/CodeConverter.java [new file with mode: 0644]
src/main/javassist/CtArray.java [new file with mode: 0644]
src/main/javassist/CtBehavior.java [new file with mode: 0644]
src/main/javassist/CtClass.java [new file with mode: 0644]
src/main/javassist/CtClassType.java [new file with mode: 0644]
src/main/javassist/CtConstructor.java [new file with mode: 0644]
src/main/javassist/CtField.java [new file with mode: 0644]
src/main/javassist/CtMember.java [new file with mode: 0644]
src/main/javassist/CtMethod.java [new file with mode: 0644]
src/main/javassist/CtNewClass.java [new file with mode: 0644]
src/main/javassist/CtNewConstructor.java [new file with mode: 0644]
src/main/javassist/CtNewMethod.java [new file with mode: 0644]
src/main/javassist/CtNewWrappedConstructor.java [new file with mode: 0644]
src/main/javassist/CtNewWrappedMethod.java [new file with mode: 0644]
src/main/javassist/CtPrimitiveType.java [new file with mode: 0644]
src/main/javassist/Dump.java [new file with mode: 0644]
src/main/javassist/Loader.java [new file with mode: 0644]
src/main/javassist/Modifier.java [new file with mode: 0644]
src/main/javassist/NotFoundException.java [new file with mode: 0644]
src/main/javassist/SerialVersionUID.java [new file with mode: 0644]
src/main/javassist/Translator.java [new file with mode: 0644]
src/main/javassist/URLClassPath.java [new file with mode: 0644]
src/main/javassist/bytecode/AccessFlag.java [new file with mode: 0644]
src/main/javassist/bytecode/AttributeInfo.java [new file with mode: 0644]
src/main/javassist/bytecode/BadBytecode.java [new file with mode: 0644]
src/main/javassist/bytecode/ByteArray.java [new file with mode: 0644]
src/main/javassist/bytecode/Bytecode.java [new file with mode: 0644]
src/main/javassist/bytecode/ClassFile.java [new file with mode: 0644]
src/main/javassist/bytecode/ClassFileWriter.java [new file with mode: 0644]
src/main/javassist/bytecode/CodeAttribute.java [new file with mode: 0644]
src/main/javassist/bytecode/CodeIterator.java [new file with mode: 0644]
src/main/javassist/bytecode/ConstPool.java [new file with mode: 0644]
src/main/javassist/bytecode/ConstantAttribute.java [new file with mode: 0644]
src/main/javassist/bytecode/Descriptor.java [new file with mode: 0644]
src/main/javassist/bytecode/ExceptionTable.java [new file with mode: 0644]
src/main/javassist/bytecode/ExceptionsAttribute.java [new file with mode: 0644]
src/main/javassist/bytecode/FieldInfo.java [new file with mode: 0644]
src/main/javassist/bytecode/InnerClassesAttribute.java [new file with mode: 0644]
src/main/javassist/bytecode/LineNumberAttribute.java [new file with mode: 0644]
src/main/javassist/bytecode/LongVector.java [new file with mode: 0644]
src/main/javassist/bytecode/MethodInfo.java [new file with mode: 0644]
src/main/javassist/bytecode/Mnemonic.java [new file with mode: 0644]
src/main/javassist/bytecode/Opcode.java [new file with mode: 0644]
src/main/javassist/bytecode/SourceFileAttribute.java [new file with mode: 0644]
src/main/javassist/bytecode/SyntheticAttribute.java [new file with mode: 0644]
src/main/javassist/compiler/CodeGen.java [new file with mode: 0644]
src/main/javassist/compiler/CompileError.java [new file with mode: 0644]
src/main/javassist/compiler/Javac.java [new file with mode: 0644]
src/main/javassist/compiler/JvstCodeGen.java [new file with mode: 0644]
src/main/javassist/compiler/KeywordTable.java [new file with mode: 0644]
src/main/javassist/compiler/Lex.java [new file with mode: 0644]
src/main/javassist/compiler/MemberCodeGen.java [new file with mode: 0644]
src/main/javassist/compiler/NoFieldException.java [new file with mode: 0644]
src/main/javassist/compiler/Parser.java [new file with mode: 0644]
src/main/javassist/compiler/ProceedHandler.java [new file with mode: 0644]
src/main/javassist/compiler/SymbolTable.java [new file with mode: 0644]
src/main/javassist/compiler/SyntaxError.java [new file with mode: 0644]
src/main/javassist/compiler/TokenId.java [new file with mode: 0644]
src/main/javassist/compiler/ast/ASTList.java [new file with mode: 0644]
src/main/javassist/compiler/ast/ASTree.java [new file with mode: 0644]
src/main/javassist/compiler/ast/AssignExpr.java [new file with mode: 0644]
src/main/javassist/compiler/ast/BinExpr.java [new file with mode: 0644]
src/main/javassist/compiler/ast/CastExpr.java [new file with mode: 0644]
src/main/javassist/compiler/ast/CondExpr.java [new file with mode: 0644]
src/main/javassist/compiler/ast/Declarator.java [new file with mode: 0644]
src/main/javassist/compiler/ast/DoubleConst.java [new file with mode: 0644]
src/main/javassist/compiler/ast/Expr.java [new file with mode: 0644]
src/main/javassist/compiler/ast/FieldDecl.java [new file with mode: 0644]
src/main/javassist/compiler/ast/InstanceOfExpr.java [new file with mode: 0644]
src/main/javassist/compiler/ast/IntConst.java [new file with mode: 0644]
src/main/javassist/compiler/ast/Keyword.java [new file with mode: 0644]
src/main/javassist/compiler/ast/Member.java [new file with mode: 0644]
src/main/javassist/compiler/ast/MethodDecl.java [new file with mode: 0644]
src/main/javassist/compiler/ast/NewExpr.java [new file with mode: 0644]
src/main/javassist/compiler/ast/Pair.java [new file with mode: 0644]
src/main/javassist/compiler/ast/Stmnt.java [new file with mode: 0644]
src/main/javassist/compiler/ast/StringL.java [new file with mode: 0644]
src/main/javassist/compiler/ast/Symbol.java [new file with mode: 0644]
src/main/javassist/compiler/ast/Variable.java [new file with mode: 0644]
src/main/javassist/compiler/ast/Visitor.java [new file with mode: 0644]
src/main/javassist/convert/TransformAfter.java [new file with mode: 0644]
src/main/javassist/convert/TransformBefore.java [new file with mode: 0644]
src/main/javassist/convert/TransformCall.java [new file with mode: 0644]
src/main/javassist/convert/TransformFieldAccess.java [new file with mode: 0644]
src/main/javassist/convert/TransformNew.java [new file with mode: 0644]
src/main/javassist/convert/TransformReadField.java [new file with mode: 0644]
src/main/javassist/convert/TransformWriteField.java [new file with mode: 0644]
src/main/javassist/convert/Transformer.java [new file with mode: 0644]
src/main/javassist/expr/Cast.java [new file with mode: 0644]
src/main/javassist/expr/Expr.java [new file with mode: 0644]
src/main/javassist/expr/ExprEditor.java [new file with mode: 0644]
src/main/javassist/expr/FieldAccess.java [new file with mode: 0644]
src/main/javassist/expr/Instanceof.java [new file with mode: 0644]
src/main/javassist/expr/MethodCall.java [new file with mode: 0644]
src/main/javassist/expr/NewExpr.java [new file with mode: 0644]
src/main/javassist/preproc/Assistant.java [new file with mode: 0644]
src/main/javassist/preproc/Compiler.java [new file with mode: 0644]
src/main/javassist/reflect/CannotCreateException.java [new file with mode: 0644]
src/main/javassist/reflect/CannotInvokeException.java [new file with mode: 0644]
src/main/javassist/reflect/ClassMetaobject.java [new file with mode: 0644]
src/main/javassist/reflect/Compiler.java [new file with mode: 0644]
src/main/javassist/reflect/Loader.java [new file with mode: 0644]
src/main/javassist/reflect/Metalevel.java [new file with mode: 0644]
src/main/javassist/reflect/Metaobject.java [new file with mode: 0644]
src/main/javassist/reflect/Reflection.java [new file with mode: 0644]
src/main/javassist/reflect/Sample.java [new file with mode: 0644]
src/main/javassist/rmi/AppletServer.java [new file with mode: 0644]
src/main/javassist/rmi/ObjectImporter.java [new file with mode: 0644]
src/main/javassist/rmi/ObjectNotFoundException.java [new file with mode: 0644]
src/main/javassist/rmi/Proxy.java [new file with mode: 0644]
src/main/javassist/rmi/RemoteException.java [new file with mode: 0644]
src/main/javassist/rmi/RemoteRef.java [new file with mode: 0644]
src/main/javassist/rmi/Sample.java [new file with mode: 0644]
src/main/javassist/rmi/StubGenerator.java [new file with mode: 0644]
src/main/javassist/runtime/Cflow.java [new file with mode: 0644]
src/main/javassist/runtime/Desc.java [new file with mode: 0644]
src/main/javassist/web/BadHttpRequest.java [new file with mode: 0644]
src/main/javassist/web/Viewer.java [new file with mode: 0644]
src/main/javassist/web/Webserver.java [new file with mode: 0644]
tutorial/brown.css [new file with mode: 0644]
tutorial/overview.gif [new file with mode: 0644]
tutorial/sequence.gif [new file with mode: 0644]
tutorial/tutorial.html [new file with mode: 0644]
tutorial/tutorial2.html [new file with mode: 0644]
tutorial/two.gif [new file with mode: 0644]

diff --git a/License.html b/License.html
new file mode 100644 (file)
index 0000000..dda30cc
--- /dev/null
@@ -0,0 +1,18 @@
+<HTML>\r
+<HEAD>\r
+<TITLE>Javassist License</TITLE>\r
+</HEAD>\r
+\r
+<BODY text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000ee bgColor=#ffffff>\r
+<p>\r
+Javassist, a Java-bytecode translator toolkit.\r
+Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved.\r
+</p>\r
+<p>\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+</p>\r
+</BODY>\r
+</HTML>\r
diff --git a/Readme.html b/Readme.html
new file mode 100644 (file)
index 0000000..cfb7312
--- /dev/null
@@ -0,0 +1,453 @@
+<html>\r
+<HEAD>\r
+   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">\r
+   <TITLE>Read Me First</TITLE>\r
+</HEAD>\r
+<body>\r
+\r
+<h1>Javassist version 2</h1>\r
+\r
+<h3>in February, 2003.\r
+<br>Copyright (C) 2000-2003 by Shigeru Chiba, All rights reserved.</h3>\r
+\r
+<p><br></p>\r
+\r
+<p>Javassist (JAVA programming ASSISTant) is yet another reflective\r
+system for Java.  It is a class library for editing bytecodes in Java;\r
+it enables Java programs to define a new class at runtime and to\r
+modify a class file when the JVM loads it.  Unlike other\r
+similar bytecode editors, Javassist provides two levels of API:\r
+source level and bytecode level.  If the users use the source-level\r
+API, they can edit a class file without knowledge of the specifications\r
+of the Java bytecode.  The whole API is designed with only the vocabulary\r
+of the Java language.  On the other hand, the bytecode-level API allows\r
+the users to directly edit a class file.\r
+\r
+<p><br>\r
+\r
+<h2>Files</h2>\r
+\r
+<ul>\r
+<table>\r
+<tr>\r
+<td><li><tt><a href="License.html">License.html</a></tt></td>\r
+<td>License file</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><li><tt><a href="tutorial/tutorial.html">tutorial/tutorial.html</a></tt></td>\r
+<td>Tutorial</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><li><tt>./javassist.jar</tt></td>\r
+<td>The Javassist jar file (class files)</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><li><tt>./javassist-src.zip</tt></td>\r
+<td>The source archive</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><li><tt><a href="html/index.html">html/index.html</a></tt></tr>\r
+<td>The top page of the Javassist API document.</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><li><tt>./sample/</tt></td>\r
+<td>Sample programs</td>\r
+</tr>\r
+</table>\r
+</ul>\r
+\r
+<p>To extract source files from the archive, use the jar command:<br>\r
+\r
+<ul><pre>% jar xvf javassist-src.zip</pre></ul>\r
+\r
+\r
+<p><br>\r
+\r
+<h2>How to run sample programs</h2>\r
+\r
+<p>JDK 1.2.2 or later is needed.\r
+\r
+<h3>1. Move to the directory where this Readme.html file is located.</h3>\r
+\r
+<p>In the description below, we assume that the platform is JDK 1.2\r
+(or later) for Solaris.  If the platform is JDK 1.2 (or later) for\r
+Windows, the class-path option is:\r
+\r
+<ul><pre>\r
+-classpath ".;javassist.jar"\r
+</pre></ul>\r
+\r
+<p>If you don't want to put the class-path option, copy\r
+<tt>./javassist.jar</tt> to\r
+\r
+<ul>&lt;<i>java-home</i>&gt;<tt>/jre/lib/ext/</tt>.</ul>\r
+\r
+<p>&lt;<i>java-home</i>&gt; depends on the system.  It is usually\r
+<tt>/usr/local/java</tt>, <tt>c:\jdk1.2\</tt>, etc.\r
+\r
+<p>If you don't use javassist.jar, first compile Javassist:\r
+<ul><pre>\r
+% cd src\r
+% javac -d .. javassist/*.java javassist/*/*.java\r
+% cd ..\r
+</pre></ul>\r
+\r
+<p>Then you can compile and run the sample programs without the class-path\r
+option.\r
+\r
+<h3>2. sample/Test.java</h3>\r
+\r
+<p>   This is a very simple program using Javassist.\r
+\r
+<p>   To run, type the commands:\r
+\r
+<ul><pre>\r
+% javac -classpath ".:javassist.jar" sample/Test.java\r
+% java -cp ".:javassist.jar" sample.Test\r
+</pre></ul>\r
+\r
+<p>   For more details, see <a type="text/plain" href="sample/Test.java">sample/Test.java</a>\r
+\r
+<h3>3. sample/reflect/*.java</h3>\r
+\r
+<p>   This is the "verbose metaobject" example well known in reflective\r
+   programming.  This program dynamically attaches a metaobject to\r
+   a Person object.  The metaobject prints a message if a method\r
+   is called on the Person object.\r
+\r
+<p>   To run, type the commands:\r
+\r
+<ul><pre>\r
+% javac -classpath ".:javassist.jar" sample/reflect/*.java\r
+% java -cp ".:javassist.jar" javassist.reflect.Loader sample.reflect.Main Joe\r
+</pre></ul>\r
+\r
+<p>Compare this result with that of the regular execution without reflection:\r
+\r
+<ul><pre>% java -cp ".:javassist.jar" sample.reflect.Person Joe</pre></ul>\r
+\r
+<p>   For more details, see <a type="text/plain" href="sample/reflect/Main.java">sample/reflect/Main.java</a>\r
+\r
+<p>   Furthermore, the Person class can be statically modified so that\r
+   all the Person objects become reflective without sample.reflect.Main.\r
+   To do this, type the commands:\r
+\r
+<ul><pre>\r
+% java -cp ".:javassist.jar" javassist.reflect.Compiler sample.reflect.Person -m sample.reflect.VerboseMetaobj\r
+</pre></ul>\r
+\r
+<p>   Then,\r
+<ul><pre>\r
+% java -cp ".:javassist.jar" sample.reflect.Person Joe\r
+</pre></ul>\r
+\r
+<h3>4. sample/duplicate/*.java</h3>\r
+\r
+<p>   This is another example of reflective programming.\r
+\r
+<p>   To run, type the commands:\r
+\r
+<ul><pre>\r
+% javac -classpath ".:javassist.jar" sample/duplicate/*.java\r
+% java -cp ".:javassist.jar" sample.duplicate.Main\r
+</pre></ul>\r
+\r
+<p>Compare this result with that of the regular execution without reflection:\r
+\r
+<ul><pre>% java sample.duplicate.Viewer</pre></ul>\r
+\r
+<p>For more details, see\r
+<a type="text/plain" href="sample/duplicate/Main.java">sample/duplicate/Main.java</a>\r
+\r
+<h3>5. sample/vector/*.java</h3>\r
+\r
+<p>This example shows the use of Javassit for producing a class representing\r
+a vector of a given type at compile time.\r
+This is a demonstration of the use of <tt>javassist.preproc</tt> package.\r
+\r
+<p>   To run, type the commands:\r
+<ul><pre>\r
+% javac -classpath ".:javassist.jar" sample/vector/*.java\r
+% java -cp ".:javassist.jar" javassist.preproc.Compiler sample/vector/Test.j\r
+% javac sample/vector/Test.java\r
+% java sample.vector.Test\r
+</pre></ul>\r
+\r
+<p>For more details, see\r
+<a type="text/plain" href="sample/vector/Test.j">sample/vector/Test.j</a>\r
+and <a type="text/plain" href="sample/vector/VectorAssistant.java">sample/vector/VectorAssistant.java</a>\r
+\r
+<h3>6. sample/rmi/*.java</h3>\r
+\r
+<p>   This demonstrates the javassist.rmi package.\r
+\r
+<p>   To run, type the commands:\r
+<ul><pre>\r
+% javac -classpath ".:javassist.jar" sample/rmi/*.java\r
+% java -cp ".:javassist.jar" sample.rmi.Counter 5001\r
+</pre></ul>\r
+\r
+<p>   The second line starts a web server listening to port 5001.\r
+\r
+<p>   Then, open <a href="sample/rmi/webdemo.html">sample/rmi/webdemo.html</a>\r
+with a web browser running\r
+   on the local host.  (<tt>webdemo.html</tt> trys to fetch an applet from\r
+   <tt>http://localhost:5001/</tt>, which is the web server we started above.)\r
+\r
+<p>   Otherwise, run sample.rmi.CountApplet as an application:\r
+\r
+<ul><pre>\r
+% java -cp ".:javassist.jar" javassist.web.Viewer localhost 5001 sample.rmi.CountApplet\r
+</pre></ul>\r
+\r
+<h3>7. sample/evolve/*.java</h3>\r
+\r
+<p>   This is a demonstration of the class evolution mechanism implemented\r
+   with Javassist.  This mechanism enables a Java program to reload an\r
+   existing class file under some restriction.\r
+\r
+<p>   To run, type the commands:\r
+<ul><pre>\r
+% javac -classpath ".:javassist.jar" sample/evolve/*.java\r
+% java -cp ".:javassist.jar" sample.evolve.DemoLoader 5003\r
+</pre></ul>\r
+\r
+<p>   The second line starts a class loader DemoLoader, which runs a web\r
+   server DemoServer listening to port 5003.\r
+\r
+<p>   Then, open <a href="http://localhost:5003/demo.html">http://localhost:5003/demo.html</a> with a web browser running\r
+   on the local host.\r
+(Or, see <a href="sample/evolve/start.html">sample/evolve/start.html</a>.)\r
+\r
+<h3>8. Hints</h3>\r
+\r
+<p>Javassist provides a class file viewer for debugging.  For more details,\r
+see javassist.Dump.\r
+\r
+<p><br>\r
+\r
+<h2>Changes</h2>\r
+\r
+<p>- version 2.4 in February, 2003.\r
+<ul>\r
+  <li>The compiler included in Javassist did not correctly work with\r
+       interface methods.  This bug was fixed.\r
+  <li>Now javassist.bytecode.Bytecode allows more than 255 local\r
+       variables in the same method.\r
+  <li>javassist.expr.Instanceof and Cast have been added.\r
+  <li>javassist.expr.{MethodCall,NewExpr,FieldAccess,Instanceof,Cast}.where()\r
+        have been added.  They return the caller-side method surrounding the\r
+       expression.\r
+  <li>javassist.expr.{MethodCall,NewExpr,FieldAccess,Instanceof,Cast}.mayThrow()\r
+        have been added.\r
+  <li>$class has been introduced.\r
+  <li>The parameters to replaceFieldRead(), replaceFieldWrite(),\r
+      and redirectFieldAccess() in javassist.CodeConverter are changed.\r
+  <li>The compiler could not correctly handle a try-catch statement.\r
+      This bug has been fixed.\r
+</ul>\r
+\r
+<p>- version 2.3 in December, 2002.\r
+<ul>\r
+  <li>The tutorial has been revised a bit.\r
+  <li>SerialVersionUID class was donated by Bob Lee.  Thanks.\r
+  <li>CtMethod.setBody() and CtConstructor.setBody() have been added.\r
+  <li>javassist.reflect.ClassMetaobject.useContextClassLoader has been added.\r
+  If true, the reflection package does not use Class.forName() but uses\r
+  a context class loader specified by the user.\r
+  <li>$sig and $type are now available.\r
+  <li>Bugs in Bytecode.write() and read() have been fixed.\r
+</ul>\r
+\r
+<p>- version 2.2 in October, 2002.\r
+<ul>\r
+  <li>The tutorial has been revised.\r
+  <li>A new package <code>javassist.expr</code> has been added.\r
+        This is replacement of classic <code>CodeConverter</code>.\r
+  <li>javassist.ConstParameter was changed into\r
+       javassist.CtMethod.ConstParameter.\r
+  <li>javassist.FieldInitializer was renamed into\r
+       javassist.CtField.Initializer.\r
+  <li>A bug in javassist.bytecode.Bytecode.addInvokeinterface() has been\r
+       fixed.\r
+  <li>In javassist.bytecode.Bytecode, addGetfield(), addGetstatic(),\r
+       addInvokespecial(), addInvokestatic(), addInvokevirtual(),\r
+       and addInvokeinterface()\r
+       have been modified to update the current statck depth.\r
+</ul>\r
+\r
+<p>- version 2.1 in July, 2002.\r
+<ul>\r
+  <li>javassist.CtMember and javassist.CtBehavior have been added.\r
+  <li>javassist.CtClass.toBytecode() has been added.\r
+  <li>javassist.CtClass.toClass() and javassist.ClassPool.writeAsClass()\r
+       has been added.\r
+  <li>javassist.ByteArrayClassPath has been added.\r
+  <li>javassist.bytecode.Mnemonic has been added.\r
+  <li>Several bugs have been fixed.\r
+</ul>\r
+\r
+<p>- version 2.0 (major update) in November, 2001.\r
+<ul>\r
+  <li>The javassist.bytecode package has been provided.  It is a\r
+    lower-level API for directly modifying a class file although\r
+    the users must have detailed knowledge of the Java bytecode.\r
+\r
+  <li>The mechanism for creating CtClass objects have been changed.\r
+\r
+  <li>javassist.tool.Dump moves to the javassist package.\r
+</ul>\r
+\r
+<p>version 1.0 in July, 2001.\r
+<ul>\r
+  <li>javassist.reflect.Metaobject and ClassMetaobject was changed.\r
+    Now they throw the same exception that they receive from a\r
+    base-level object.\r
+</ul>\r
+\r
+<p>- version 0.8\r
+<ul>\r
+  <li>javassist.tool.Dump was added.  It is a class file viewer.\r
+\r
+  <li>javassist.FiledInitializer.byNewArray() was added.  It is for\r
+    initializing a field with an array object.\r
+\r
+  <li>javassist.CodeConverter.redirectMethodCall() was added.\r
+\r
+  <li>javassist.Run was added.\r
+</ul>\r
+\r
+<p>- version 0.7\r
+<ul>\r
+  <li>javassit.Loader was largely modified.  javassist.UserLoader was\r
+    deleted.  Instead, Codebase was renamed to ClassPath\r
+    and UserClassPath was added.  Now programmers who want to\r
+    customize Loader must write a class implementing UserClassPath\r
+    instead of UserLoader.  This change is for sharing class search paths\r
+    between Loader and CtClass.CtClass(String).\r
+\r
+  <li>CtClass.addField(), addMethod(), addConstructor(), addWrapper() were\r
+    also largely modified so that it receives CtNewMethod, CtNewConstructor,\r
+    or CtNewField.  The static methods for creating these objects were\r
+    added to the API.\r
+\r
+  <li>Constructors are now represented by CtConstructor objects.\r
+    CtConstructor is a subclass of CtMethod.\r
+\r
+  <li>CtClass.getUserAttribute() was removed.  Use CtClass.getAttribute().\r
+\r
+  <li>javassist.rmi.RmiLoader was added.\r
+\r
+  <li>javassist.reflect.Metalevel._setMetaobject() was added.  Now\r
+    metaobjects can be replaced at runtime.\r
+</ul>\r
+\r
+<p>- version 0.6\r
+<ul>\r
+  <li>Javassist was modified to correctly deal with array types appearing\r
+    in signatures.\r
+\r
+  <li>A bug crashed resulting bytecode if a class includes a private static\r
+    filed.  It has been fixed.\r
+\r
+  <li>javassist.CtNewInterface was added.\r
+\r
+  <li>javassist.Loader.recordClass() was renamed into makeClass().\r
+\r
+  <li>javassist.UserLoader.loadClass() was changed to take the second\r
+    parameter.\r
+</ul>\r
+\r
+<p>- version 0.5\r
+<ul>\r
+  <li>a bug-fix version.\r
+</ul>\r
+\r
+<p>- version 0.4\r
+<ul>\r
+  <li>Major update again.  Many classes and methods were changed.\r
+    Most of methods taking java.lang.Class have been changed to\r
+    take javassist.CtClass.\r
+</ul>\r
+\r
+<p>- version 0.3\r
+<ul>\r
+  <li>Major update.  Many classes and methods were changed.\r
+</ul>\r
+\r
+<p>- version 0.2\r
+<ul>\r
+  <li>Jar/zip files are supported.\r
+</ul>\r
+\r
+<p>-version 0.1 in April, 1999.\r
+<ul>\r
+  <li>The first release.\r
+</ul>\r
+\r
+<p><br>\r
+\r
+<h2>Bug reports etc.</h2>\r
+\r
+<dl>\r
+<dt>Bug reports:\r
+<dd><tt><a href="mailto:chiba@acm.org">chiba@acm.org</a></tt>\r
+<br>or\r
+<tt><a href="mailto:chiba@is.titech.ac.jp">chiba@is.titech.ac.jp</a></tt>\r
+<br>\r
+\r
+<p><dt>The home page of Javassist:\r
+<dd><tt><a href="http://www.csg.is.titech.ac.jp/~chiba/javassist">http://www.csg.is.titech.ac.jp/~chiba/javassist</a></tt>\r
+\r
+<p><dt>The Javassist mailing list:\r
+<dd><tt><a href="javassist@csg.is.titech.ac.jp">javassist@csg.is.titech.ac.jp</a></tt>\r
+</dl>\r
+\r
+<p><br>\r
+\r
+<h2>Copyright notices</h2>\r
+\r
+<p>This software is subject to the <a href="license.html">Mozilla Public\r
+License Version 1.1</a>.\r
+\r
+<p>Software distributed under the License is distributed on an "AS IS"\r
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See\r
+the License for the specific language governing rights and limitations\r
+under the License.\r
+\r
+<p>The Original Code is Javassist.\r
+\r
+<p>The Initial Developer of the Original Code is Shigeru Chiba.\r
+<br>Portions\r
+created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.\r
+All Rights Reserved.\r
+\r
+<p><br>\r
+\r
+<h2>Acknowledgments</h2>\r
+\r
+<p>The development of this software is sponsored in part by the PRESTO\r
+program (Sakigake Kenkyu 21) of <a href="http://www.jst.go.jp/">Japan\r
+Science and Technology Corporation</a>.\r
+\r
+<p>I'd like to thank Michiaki Tatsubori, Johan Cloetens,\r
+Philip Tomlinson, Alex Villazon, Pascal Rapicault, Dan HE, Eric Tanter,\r
+Michael Haupt, Toshiyuki Sasaki, Renaud Pawlak, Luc Bourlier,\r
+Eric Bui, Lewis Stiller, Susumu Yamazaki, Rodrigo Teruo Tomita,\r
+Marc Segura-Devillechaise, Jan Baudisch, Julien Blass, Yoshiki Sato,\r
+Fabian Crabus, Bo Norregaard Jorgensen, Bob Lee, Bill Burke,\r
+Remy Sanlaville, Muga Nishizawa, Alexey Loubyansky, Saori Oki,\r
+Andreas Salathe, and Dante Torres estrada for their contributions.\r
+\r
+<p><br>\r
+\r
+<hr>\r
+<a href="http://www.is.titech.ac.jp/~chiba">Shigeru Chiba</a>\r
+(Email: <tt>chiba@is.titech.ac.jp</tt>)\r
+<br>Dept. of Math. and Computing Sciences,\r
+<a href="http://www.titech.ac.jp">Tokyo Institute of Technology</a>\r
diff --git a/build.xml b/build.xml
new file mode 100644 (file)
index 0000000..5d57a3a
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>\r
+\r
+<!-- ======================================================================= -->\r
+<!-- JBoss build file                                                       -->\r
+<!-- ======================================================================= -->\r
+\r
+<project name="javassist" default="jar" basedir=".">\r
+\r
+    <property environment="env"/>\r
+    <property name="src.dir" value="${basedir}/src/main"/>\r
+    <property name="build.dir" value="${basedir}/build"/>\r
+    <property name="build.classes.dir" value="${build.dir}/classes"/>\r
+\r
+  <!-- Build classpath -->\r
+  <path id="classpath">\r
+       <pathelement location="${build.classes.dir}"/>\r
+  </path>\r
+\r
+  <property name="build.classpath" refid="classpath"/>\r
+\r
+  <!-- =================================================================== -->\r
+  <!-- Prepares the build directory                                        -->\r
+  <!-- =================================================================== -->\r
+  <target name="prepare" >\r
+    <mkdir dir="${build.dir}"/>\r
+    <mkdir dir="${build.classes.dir}"/>\r
+  </target>\r
+\r
+  <!-- =================================================================== -->\r
+  <!-- Compiles the source code                                            -->\r
+  <!-- =================================================================== -->\r
+  <target name="compile" depends="prepare">\r
+    <javac srcdir="${src.dir}"\r
+           destdir="${build.classes.dir}"\r
+           debug="on"\r
+           deprecation="on"\r
+           optimize="off"\r
+           includes="**">\r
+            <classpath refid="classpath"/>\r
+    </javac>\r
+  </target>\r
+\r
+  <target name="jar" depends="compile">\r
+    <jar jarfile="javassist.jar">\r
+      <fileset dir="${build.classes.dir}">\r
+         <include name="**/*.class"/>\r
+      </fileset>\r
+     </jar>\r
+  </target>\r
+\r
+  <target name="javadocs">\r
+    <javadoc packagenames="javassist.*"\r
+           sourcepath="src/main"\r
+           defaultexcludes="yes"\r
+           destdir="html"\r
+           author="true"\r
+           version="true"\r
+           use="true"\r
+           windowtitle="Javassist API">\r
+      <doctitle><![CDATA[<h1>Javassist</h1>]]></doctitle>\r
+      <bottom><![CDATA[<i>Javassist, a Java-bytecode translator toolkit. \r
+Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved.</i>]]></bottom>\r
+    </javadoc>\r
+  </target>\r
+\r
+\r
+  <target name="dist" depends="jar,javadocs">\r
+    <delete file="javassist-dist.zip"/>\r
+    <zip zipfile="javassist-dist.zip">\r
+       <fileset dir="${basedir}">\r
+          <include name="**"/>\r
+          <exclude name="build/**"/>\r
+          <exclude name="javassist-dist.zip"/>\r
+       </fileset>\r
+    </zip>\r
+  </target>\r
+\r
+  <target name="clean">\r
+    <delete dir="build"/>\r
+    <delete dir="html"/>\r
+    <delete file="javassist.jar"/>\r
+    <delete file="javassist-dist.zip"/>\r
+  </target>\r
+</project>\r
+\r
diff --git a/sample/Test.java b/sample/Test.java
new file mode 100644 (file)
index 0000000..0692943
--- /dev/null
@@ -0,0 +1,44 @@
+package sample;\r
+\r
+import javassist.*;\r
+\r
+/*\r
+   A very simple sample program\r
+\r
+   This program overwrites sample/Test.class (the class file of this\r
+   class itself) for adding a method g().  If the method g() is not\r
+   defined in class Test, then this program adds a copy of\r
+   f() to the class Test with name g().  Otherwise, this program does\r
+   not modify sample/Test.class at all.\r
+\r
+   To see the modified class definition, execute:\r
+\r
+   % javap sample.Test\r
+\r
+   after running this program.\r
+*/\r
+public class Test {\r
+    public int f(int i) {\r
+       return i + 1;\r
+    }\r
+\r
+    public static void main(String[] args) throws Exception {\r
+       ClassPool pool = ClassPool.getDefault(null);\r
+\r
+       CtClass cc = pool.get("sample.Test");\r
+       try {\r
+           cc.getDeclaredMethod("g");\r
+           System.out.println("g() is already defined in sample.Test.");\r
+       }\r
+       catch (NotFoundException e) {\r
+           /* getDeclaredMethod() throws an exception if g()\r
+            * is not defined in sample.Test.\r
+            */\r
+           CtMethod fMethod = cc.getDeclaredMethod("f");\r
+           CtMethod gMethod = CtNewMethod.copy(fMethod, "g", cc, null);\r
+           cc.addMethod(gMethod);\r
+           pool.writeFile("sample.Test");      // update the class file\r
+           System.out.println("g() was added.");\r
+       }\r
+    }\r
+}\r
diff --git a/sample/duplicate/Ball.java b/sample/duplicate/Ball.java
new file mode 100644 (file)
index 0000000..21d6e1c
--- /dev/null
@@ -0,0 +1,44 @@
+package sample.duplicate;\r
+\r
+import java.awt.Graphics;\r
+import java.awt.Color;\r
+\r
+public class Ball {\r
+    private int x, y;\r
+    private Color color;\r
+    private int radius = 30;\r
+    private boolean isBackup = false;\r
+\r
+    public Ball(int x, int y) {\r
+       move(x, y);\r
+       changeColor(Color.orange);\r
+    }\r
+\r
+    // This constructor is for a backup object.\r
+    public Ball(Ball b) {\r
+       isBackup = true;\r
+    }\r
+\r
+    // Adjust the position so that the backup object is visible.\r
+    private void adjust() {\r
+       if (isBackup) {\r
+           this.x += 50;\r
+           this.y += 50;\r
+       }\r
+    }\r
+\r
+    public void paint(Graphics g) {\r
+       g.setColor(color);\r
+       g.fillOval(x, y, radius, radius);\r
+    }\r
+\r
+    public void move(int x, int y) {\r
+       this.x = x;\r
+       this.y = y;\r
+       adjust();\r
+    }\r
+\r
+    public void changeColor(Color color) {\r
+       this.color = color;\r
+    }\r
+}\r
diff --git a/sample/duplicate/DuplicatedObject.java b/sample/duplicate/DuplicatedObject.java
new file mode 100644 (file)
index 0000000..5995abc
--- /dev/null
@@ -0,0 +1,38 @@
+package sample.duplicate;\r
+\r
+import javassist.reflect.*;\r
+\r
+public class DuplicatedObject extends Metaobject {\r
+    private DuplicatedObject backup;\r
+\r
+    // if a base-level object is created, this metaobject creates\r
+    // a copy of the base-level object.\r
+\r
+    public DuplicatedObject(Object self, Object[] args) {\r
+       super(self, args);\r
+       ClassMetaobject clazz = getClassMetaobject();\r
+       if (clazz.isInstance(args[0]))\r
+           backup = null;      // self is a backup object.\r
+       else {\r
+           Object[] args2 = new Object[1];\r
+           args2[0] = self;\r
+           try {\r
+               Metalevel m = (Metalevel)clazz.newInstance(args2);\r
+               backup = (DuplicatedObject)m._getMetaobject();\r
+           }\r
+           catch (CannotCreateException e) {\r
+               backup = null;\r
+           }\r
+       }\r
+    }\r
+\r
+    public Object trapMethodcall(int identifier, Object[] args) \r
+       throws Throwable\r
+    {\r
+       Object obj = super.trapMethodcall(identifier, args);\r
+       if (backup != null)\r
+           backup.trapMethodcall(identifier, args);\r
+\r
+       return obj;\r
+    }\r
+}\r
diff --git a/sample/duplicate/Main.java b/sample/duplicate/Main.java
new file mode 100644 (file)
index 0000000..e152a23
--- /dev/null
@@ -0,0 +1,44 @@
+package sample.duplicate;\r
+\r
+/*\r
+  Runtime metaobject (JDK 1.2 or later only).\r
+\r
+  With the javassist.reflect package, the users can attach a metaobject\r
+  to an object.  The metaobject can control the behavior of the object.\r
+  For example, you can implement fault tolerancy with this ability.  One\r
+  of the implementation techniques of fault tolernacy is to make a copy\r
+  of every object containing important data and maintain it as a backup.\r
+  If the machine running the object becomes down, the backup object on a\r
+  different machine is used to continue the execution.\r
+\r
+  To make the copy of the object a real backup, all the method calls to\r
+  the object must be also sent to that copy.  The metaobject is needed\r
+  for this duplication of the method calls.  It traps every method call\r
+  and invoke the same method on the copy of the object so that the\r
+  internal state of the copy is kept equivalent to that of the original\r
+  object.\r
+\r
+  First, run sample.duplicate.Viewer without a metaobject.\r
+\r
+  % java sample.duplicate.Viewer\r
+\r
+  This program shows a ball in a window.\r
+\r
+  Then, run the same program with a metaobject, which is an instance\r
+  of sample.duplicate.DuplicatedObject.\r
+\r
+  % java sample.duplicate.Main\r
+\r
+  You would see two balls in a window.  This is because\r
+  sample.duplicate.Viewer is loaded by javassist.reflect.Loader so that\r
+  a metaobject would be attached.\r
+*/\r
+public class Main {\r
+    public static void main(String[] args) throws Throwable {\r
+       javassist.reflect.Loader cl = new javassist.reflect.Loader();\r
+       cl.makeReflective("sample.duplicate.Ball",\r
+                         "sample.duplicate.DuplicatedObject",\r
+                         "javassist.reflect.ClassMetaobject");\r
+       cl.run("sample.duplicate.Viewer", args);\r
+    }\r
+}\r
diff --git a/sample/duplicate/Viewer.java b/sample/duplicate/Viewer.java
new file mode 100644 (file)
index 0000000..aec13f6
--- /dev/null
@@ -0,0 +1,78 @@
+package sample.duplicate;\r
+\r
+import java.applet.*;\r
+import java.awt.*;\r
+import java.awt.event.*;\r
+\r
+public class Viewer extends Applet\r
+    implements MouseListener, ActionListener, WindowListener\r
+{\r
+    private static final Color[] colorList = {\r
+       Color.orange, Color.pink, Color.green, Color.blue };\r
+\r
+    private Ball ball;\r
+    private int colorNo;\r
+\r
+    public void init() {\r
+       colorNo = 0;\r
+       Button b = new Button("change");\r
+       b.addActionListener(this);\r
+       add(b);\r
+\r
+       addMouseListener(this);\r
+    }\r
+\r
+    public void start() {\r
+       ball = new Ball(50, 50);\r
+       ball.changeColor(colorList[0]);\r
+    }\r
+\r
+    public void paint(Graphics g) {\r
+       ball.paint(g);\r
+    }\r
+\r
+    public void mouseClicked(MouseEvent ev) {\r
+       ball.move(ev.getX(), ev.getY());\r
+       repaint();\r
+    }\r
+\r
+    public void mouseEntered(MouseEvent ev) {}\r
+\r
+    public void mouseExited(MouseEvent ev) {}\r
+\r
+    public void mousePressed(MouseEvent ev) {}\r
+\r
+    public void mouseReleased(MouseEvent ev) {}\r
+\r
+    public void actionPerformed(ActionEvent e) {\r
+       ball.changeColor(colorList[++colorNo % colorList.length]);\r
+       repaint();\r
+    }\r
+\r
+    public void windowOpened(WindowEvent e) {}\r
+\r
+    public void windowClosing(WindowEvent e) {\r
+       System.exit(0);\r
+    }\r
+\r
+    public void windowClosed(WindowEvent e) {}\r
+\r
+    public void windowIconified(WindowEvent e) {}\r
+\r
+    public void windowDeiconified(WindowEvent e) {}\r
+\r
+    public void windowActivated(WindowEvent e) {}\r
+\r
+    public void windowDeactivated(WindowEvent e) {}\r
+\r
+    public static void main(String[] args) {\r
+       Frame f = new Frame("Viewer");\r
+       Viewer view = new Viewer();\r
+       f.addWindowListener(view);\r
+       f.add(view);\r
+       f.setSize(300, 300);\r
+       view.init();\r
+       view.start();\r
+       f.setVisible(true);\r
+    }\r
+}\r
diff --git a/sample/evolve/CannotCreateException.java b/sample/evolve/CannotCreateException.java
new file mode 100644 (file)
index 0000000..828afb0
--- /dev/null
@@ -0,0 +1,14 @@
+package sample.evolve;\r
+\r
+/**\r
+ * Signals that VersionManager.newInstance() fails.\r
+ */\r
+public class CannotCreateException extends RuntimeException {\r
+    public CannotCreateException(String s) {\r
+       super(s);\r
+    }\r
+\r
+    public CannotCreateException(Exception e) {\r
+       super("by " + e.toString());\r
+    }\r
+}\r
diff --git a/sample/evolve/CannotUpdateException.java b/sample/evolve/CannotUpdateException.java
new file mode 100644 (file)
index 0000000..bd28bba
--- /dev/null
@@ -0,0 +1,14 @@
+package sample.evolve;\r
+\r
+/**\r
+ * Signals that VersionManager.update() fails.\r
+ */\r
+public class CannotUpdateException extends Exception {\r
+    public CannotUpdateException(String msg) {\r
+       super(msg);\r
+    }\r
+\r
+    public CannotUpdateException(Exception e) {\r
+       super("by " + e.toString());\r
+    }\r
+}\r
diff --git a/sample/evolve/DemoLoader.java b/sample/evolve/DemoLoader.java
new file mode 100644 (file)
index 0000000..5fa950d
--- /dev/null
@@ -0,0 +1,40 @@
+package sample.evolve;\r
+\r
+import javassist.*;\r
+import java.io.*;\r
+import java.util.Hashtable;\r
+\r
+/**\r
+ * DemoLoader is a class loader for running a program including\r
+ * an updatable class.  This simple loader allows only a single\r
+ * class to be updatable.  (Extending it for supporting multiple\r
+ * updatable classes is easy.)\r
+ *\r
+ * To run, type as follows:\r
+ *\r
+ * % java sample.evolve.DemoLoader <port number>\r
+ *\r
+ * Then DemoLoader launches sample.evolve.DemoServer with <port number>.\r
+ * It also translates sample.evolve.WebPage, which sample.evolve.DemoServer\r
+ * uses, so that it is an updable class.\r
+ *\r
+ * Note: JDK 1.2 or later only.\r
+ */\r
+public class DemoLoader {\r
+    private static final int initialVersion = 0;\r
+    private String updatableClassName = null;\r
+    private CtClass updatableClass = null;\r
+\r
+    /* Creates a <code>DemoLoader</code> for making class WebPage\r
+     * updatable.  Then it runs main() in sample.evolve.DemoServer.\r
+     */\r
+    public static void main(String[] args) throws Throwable {\r
+       Evolution translator = new Evolution();\r
+       ClassPool cp = ClassPool.getDefault(translator);\r
+       Loader cl = new Loader();\r
+       cl.setClassPool(cp);\r
+\r
+       translator.makeUpdatable("sample.evolve.WebPage");\r
+       cl.run("sample.evolve.DemoServer", args);\r
+    }\r
+}\r
diff --git a/sample/evolve/DemoServer.java b/sample/evolve/DemoServer.java
new file mode 100644 (file)
index 0000000..943c509
--- /dev/null
@@ -0,0 +1,102 @@
+package sample.evolve;\r
+\r
+import javassist.*;\r
+import javassist.web.*;\r
+import java.io.*;\r
+\r
+/**\r
+ * A web server for demonstrating class evolution.  It must be\r
+ * run with a DemoLoader.\r
+ *\r
+ * If a html file /java.html is requested, this web server calls\r
+ * WebPage.show() for constructing the contents of that html file\r
+ * So if a DemoLoader changes the definition of WebPage, then\r
+ * the image of /java.html is also changed.\r
+ * Note that WebPage is not an applet.  It is rather\r
+ * similar to a CGI script or a servlet.  The web server never\r
+ * sends the class file of WebPage to web browsers.\r
+ *\r
+ * Furthermore, if a html file /update.html is requested, this web\r
+ * server overwrites WebPage.class (class file) and calls update()\r
+ * in VersionManager so that WebPage.class is loaded into the JVM\r
+ * again.  The new contents of WebPage.class are copied from\r
+ * either WebPage.class.0 or WebPage.class.1.\r
+ */\r
+public class DemoServer extends Webserver {\r
+\r
+    public static void main(String[] args) throws IOException\r
+    {\r
+       if (args.length == 1) {\r
+           DemoServer web = new DemoServer(Integer.parseInt(args[0]));\r
+           web.run();\r
+       }\r
+       else\r
+           System.err.println(\r
+               "Usage: java sample.evolve.DemoServer <port number>");\r
+    }\r
+\r
+    public DemoServer(int port) throws IOException {\r
+       super(port);\r
+       htmlfileBase = "sample/evolve/";\r
+    }\r
+\r
+    private static final String ver0 = "sample/evolve/WebPage.class.0";\r
+    private static final String ver1 = "sample/evolve/WebPage.class.1";\r
+    private String currentVersion = ver0;\r
+\r
+    public void doReply(InputStream in, OutputStream out, String cmd)\r
+       throws IOException, BadHttpRequest\r
+    {\r
+       if (cmd.startsWith("GET /java.html ")) {\r
+           runJava(out);\r
+           return;\r
+       }\r
+       else if (cmd.startsWith("GET /update.html ")) {\r
+           try {\r
+               if (currentVersion == ver0)\r
+                   currentVersion = ver1;\r
+               else\r
+                   currentVersion = ver0;\r
+\r
+               updateClassfile(currentVersion);\r
+               VersionManager.update("sample.evolve.WebPage");\r
+           }\r
+           catch (CannotUpdateException e) {\r
+               logging(e.toString());\r
+           }\r
+           catch (FileNotFoundException e) {\r
+               logging(e.toString());\r
+           }\r
+       }\r
+\r
+       super.doReply(in, out, cmd);\r
+    }\r
+\r
+    private void runJava(OutputStream outs) throws IOException {\r
+       OutputStreamWriter out = new OutputStreamWriter(outs);\r
+       out.write("HTTP/1.0 200 OK\r\n\r\n");\r
+       WebPage page = new WebPage();\r
+       page.show(out);\r
+       out.close();\r
+    }\r
+\r
+    /* updateClassfile() copies the specified file to WebPage.class.\r
+     */\r
+    private void updateClassfile(String filename)\r
+       throws IOException, FileNotFoundException\r
+    {\r
+       byte[] buf = new byte[1024];\r
+\r
+       FileInputStream fin\r
+           = new FileInputStream(filename);\r
+       FileOutputStream fout\r
+           = new FileOutputStream("sample/evolve/WebPage.class");\r
+       for (;;) {\r
+           int len = fin.read(buf);\r
+           if (len >= 0)\r
+               fout.write(buf, 0, len);\r
+           else\r
+               break;\r
+       }\r
+    }\r
+}\r
diff --git a/sample/evolve/Evolution.java b/sample/evolve/Evolution.java
new file mode 100644 (file)
index 0000000..810f198
--- /dev/null
@@ -0,0 +1,203 @@
+package sample.evolve;\r
+\r
+import javassist.*;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * Evolution provides a set of methods for instrumenting bytecodes.\r
+ *\r
+ * For class evolution, updatable class A is renamed to B.  Then an\r
+ * abstract class named A is produced as the super class of B.  If the\r
+ * original class A has a public method m(), then the abstract class A\r
+ * has an abstract method m().\r
+ *\r
+ *                    abstract class A\r
+ *                      abstract m()\r
+ *                      _makeInstance()\r
+ *                           |\r
+ *     class A  --------> class B\r
+ *       m()                m()\r
+ *\r
+ * Also, all the other classes are translated so that "new A(i)"\r
+ * in the methods is replaced with "_makeInstance(i)".  This makes\r
+ * it possible to change the behavior of the instantiation of\r
+ * the class A.\r
+ */\r
+public class Evolution implements Translator {\r
+    public final static String handlerMethod = "_makeInstance";\r
+    public final static String latestVersionField\r
+                               = VersionManager.latestVersionField;\r
+    public final static String versionManagerMethod = "initialVersion";\r
+\r
+    private static CtMethod trapMethod;\r
+    private static final int initialVersion = 0;\r
+    private ClassPool pool;\r
+    private String updatableClassName = null;\r
+    private CtClass updatableClass = null;\r
+\r
+    public void start(ClassPool _pool) throws NotFoundException {\r
+       pool = _pool;\r
+\r
+       // Get the definition of Sample.make() and store it into trapMethod\r
+       // for later use.\r
+       trapMethod = _pool.getMethod("sample.evolve.Sample", "make");\r
+    }\r
+\r
+    public void onWrite(ClassPool _pool, String classname)\r
+       throws NotFoundException, CannotCompileException\r
+    {\r
+       onWriteUpdatable(classname);\r
+\r
+       /*\r
+        * Replaces all the occurrences of the new operator with a call\r
+        * to _makeInstance().\r
+        */\r
+       CtClass clazz = _pool.get(classname);\r
+       CtClass absClass = updatableClass;\r
+       CodeConverter converter = new CodeConverter();\r
+       converter.replaceNew(absClass, absClass, handlerMethod);\r
+       clazz.instrument(converter);\r
+    }\r
+\r
+    private void onWriteUpdatable(String classname)\r
+       throws NotFoundException, CannotCompileException\r
+    {\r
+       // if the class is a concrete class,\r
+       // classname is <updatableClassName>$<version>.\r
+\r
+       int i = classname.lastIndexOf('$');\r
+       if (i <= 0)\r
+           return;\r
+\r
+       String orgname = classname.substring(0, i);\r
+       if (!orgname.equals(updatableClassName))\r
+           return;\r
+\r
+       int version;\r
+       try {\r
+           version = Integer.parseInt(classname.substring(i + 1));\r
+       }\r
+       catch (NumberFormatException e) {\r
+           throw new NotFoundException(classname, e);\r
+       }\r
+\r
+\r
+       CtClass clazz = pool.getAndRename(orgname, classname);\r
+       makeConcreteClass(clazz, updatableClass, version);\r
+    }\r
+\r
+    /* Register an updatable class.\r
+     */\r
+    public void makeUpdatable(String classname)\r
+       throws NotFoundException, CannotCompileException\r
+    {\r
+       if (pool == null)\r
+           throw new RuntimeException(\r
+                      "Evolution has not been linked to ClassPool.");\r
+\r
+       CtClass c = pool.get(classname);\r
+       updatableClassName = classname;\r
+       updatableClass = makeAbstractClass(c);\r
+    }\r
+\r
+    /**\r
+     * Produces an abstract class.\r
+     */\r
+    protected CtClass makeAbstractClass(CtClass clazz)\r
+       throws CannotCompileException, NotFoundException\r
+    {\r
+       int i;\r
+\r
+       CtClass absClass = pool.makeClass(clazz.getName());\r
+       absClass.setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT);\r
+       absClass.setSuperclass(clazz.getSuperclass());\r
+       absClass.setInterfaces(clazz.getInterfaces());\r
+\r
+       // absClass.inheritAllConstructors();\r
+\r
+       CtField fld = new CtField(pool.get("java.lang.Class"),\r
+                                 latestVersionField, absClass);\r
+       fld.setModifiers(Modifier.PUBLIC | Modifier.STATIC);\r
+\r
+       CtField.Initializer finit\r
+           = CtField.Initializer.byCall(\r
+               pool.get("sample.evolve.VersionManager"),\r
+               versionManagerMethod,\r
+               new String[] { clazz.getName() });\r
+       absClass.addField(fld, finit);\r
+\r
+       CtField[] fs = clazz.getDeclaredFields();\r
+       for (i = 0; i < fs.length; ++i) {\r
+           CtField f = fs[i];\r
+           if (Modifier.isPublic(f.getModifiers()))\r
+               absClass.addField(new CtField(f.getType(), f.getName(),\r
+                                             absClass));\r
+       }\r
+\r
+       CtConstructor[] cs = clazz.getDeclaredConstructors();\r
+       for (i = 0; i < cs.length; ++i) {\r
+           CtConstructor c = cs[i];\r
+           int mod = c.getModifiers();\r
+           if (Modifier.isPublic(mod)) {\r
+               CtMethod wm\r
+                   = CtNewMethod.wrapped(absClass, handlerMethod,\r
+                                         c.getParameterTypes(),\r
+                                         c.getExceptionTypes(),\r
+                                         trapMethod, null, absClass);\r
+               wm.setModifiers(Modifier.PUBLIC | Modifier.STATIC);\r
+               absClass.addMethod(wm);\r
+           }\r
+       }\r
+\r
+       CtMethod[] ms = clazz.getDeclaredMethods();\r
+       for (i = 0; i < ms.length; ++i) {\r
+           CtMethod m = ms[i];\r
+           int mod = m.getModifiers();\r
+           if (Modifier.isPublic(mod))\r
+               if (Modifier.isStatic(mod))\r
+                   throw new CannotCompileException(\r
+                               "static methods are not supported.");\r
+               else {\r
+                   CtMethod m2\r
+                       = CtNewMethod.abstractMethod(m.getReturnType(),\r
+                                                    m.getName(),\r
+                                                    m.getParameterTypes(),\r
+                                                    m.getExceptionTypes(),\r
+                                                    absClass);\r
+                   absClass.addMethod(m2);\r
+               }\r
+       }\r
+\r
+       return absClass;\r
+    }\r
+\r
+    /**\r
+     * Modifies the given class file so that it is a subclass of the\r
+     * abstract class produced by makeAbstractClass().\r
+     *\r
+     * Note: the naming convention must be consistent with\r
+     * VersionManager.update().\r
+     */\r
+    protected void makeConcreteClass(CtClass clazz,\r
+                                    CtClass abstractClass, int version)\r
+       throws CannotCompileException, NotFoundException\r
+    {\r
+       int i;\r
+       clazz.setSuperclass(abstractClass);\r
+       CodeConverter converter = new CodeConverter();\r
+       CtField[] fs = clazz.getDeclaredFields();\r
+       for (i = 0; i < fs.length; ++i) {\r
+           CtField f = fs[i];\r
+           if (Modifier.isPublic(f.getModifiers()))\r
+               converter.redirectFieldAccess(f, abstractClass, f.getName());\r
+       }\r
+\r
+       CtConstructor[] cs = clazz.getDeclaredConstructors();\r
+       for (i = 0; i < cs.length; ++i)\r
+           cs[i].instrument(converter);\r
+\r
+       CtMethod[] ms = clazz.getDeclaredMethods();\r
+       for (i = 0; i < ms.length; ++i)\r
+           ms[i].instrument(converter);\r
+    }\r
+}\r
diff --git a/sample/evolve/Sample.java b/sample/evolve/Sample.java
new file mode 100644 (file)
index 0000000..c147c96
--- /dev/null
@@ -0,0 +1,12 @@
+package sample.evolve;\r
+\r
+/**\r
+ * This is a sample class used by Transformer.\r
+ */\r
+public class Sample {\r
+    public static Class _version;\r
+\r
+    public static Object make(Object[] args) {\r
+       return VersionManager.make(_version, args);\r
+    }\r
+}\r
diff --git a/sample/evolve/VersionManager.java b/sample/evolve/VersionManager.java
new file mode 100644 (file)
index 0000000..d95268b
--- /dev/null
@@ -0,0 +1,90 @@
+package sample.evolve;\r
+\r
+import java.util.Hashtable;\r
+import java.lang.reflect.*;\r
+import javassist.CtClass;\r
+\r
+/**\r
+ * Runtime system for class evolution\r
+ */\r
+public class VersionManager {\r
+    private static Hashtable versionNo = new Hashtable();\r
+    public final static String latestVersionField = "_version";\r
+\r
+    /**\r
+     * For updating the definition of class my.X, say:\r
+     *\r
+     * VersionManager.update("my.X");\r
+     */\r
+    public static void update(String qualifiedClassname)\r
+       throws CannotUpdateException\r
+    {\r
+       try {\r
+           Class c = getUpdatedClass(qualifiedClassname);\r
+           Field f = c.getField(latestVersionField);\r
+           f.set(null, c);\r
+       }\r
+       catch (ClassNotFoundException e) {\r
+           throw new CannotUpdateException("cannot update class: "\r
+                                           + qualifiedClassname);\r
+       }\r
+       catch (Exception e) {\r
+           throw new CannotUpdateException(e);\r
+       }\r
+    }\r
+\r
+    private static Class getUpdatedClass(String qualifiedClassname)\r
+       throws ClassNotFoundException\r
+    {\r
+       int version;\r
+       Object found = versionNo.get(qualifiedClassname);\r
+       if (found == null)\r
+           version = 0;\r
+       else\r
+           version = ((Integer)found).intValue() + 1;\r
+\r
+       Class c = Class.forName(qualifiedClassname + '$' + version);\r
+       versionNo.put(qualifiedClassname, new Integer(version));\r
+       return c;\r
+    }\r
+\r
+    /* initiaVersion() is used to initialize the _version field of\r
+     * the updatable classes.\r
+     */\r
+    public static Class initialVersion(String[] params) {\r
+       try {\r
+           return getUpdatedClass(params[0]);\r
+       }\r
+       catch (ClassNotFoundException e) {\r
+           throw new RuntimeException("cannot initialize " + params[0]);\r
+       }\r
+    }\r
+\r
+    /** make() performs the object creation of the updatable classes.\r
+     * The expression "new <updatable class>" is replaced with a call\r
+     * to this method.\r
+     */\r
+    public static Object make(Class clazz, Object[] args) {\r
+       Constructor[] constructors = clazz.getConstructors();\r
+       int n = constructors.length;\r
+       for (int i = 0; i < n; ++i) {\r
+           try {\r
+               return constructors[i].newInstance(args);\r
+           }\r
+           catch (IllegalArgumentException e) {\r
+               // try again\r
+           }\r
+           catch (InstantiationException e) {\r
+               throw new CannotCreateException(e);\r
+           }\r
+           catch (IllegalAccessException e) {\r
+               throw new CannotCreateException(e);\r
+           }\r
+           catch (InvocationTargetException e) {\r
+               throw new CannotCreateException(e);\r
+           }\r
+       }\r
+\r
+       throw new CannotCreateException("no constructor matches");\r
+    }\r
+}\r
diff --git a/sample/evolve/WebPage.class.0 b/sample/evolve/WebPage.class.0
new file mode 100644 (file)
index 0000000..3cc1d74
Binary files /dev/null and b/sample/evolve/WebPage.class.0 differ
diff --git a/sample/evolve/WebPage.class.1 b/sample/evolve/WebPage.class.1
new file mode 100644 (file)
index 0000000..fe49380
Binary files /dev/null and b/sample/evolve/WebPage.class.1 differ
diff --git a/sample/evolve/WebPage.java b/sample/evolve/WebPage.java
new file mode 100644 (file)
index 0000000..7c2b7cf
--- /dev/null
@@ -0,0 +1,31 @@
+package sample.evolve;\r
+\r
+import java.io.*;\r
+import java.util.*;\r
+\r
+/**\r
+ * Updatable class.  DemoServer instantiates this class and calls\r
+ * show() on the created object.\r
+ */\r
+\r
+// WebPage.class.0\r
+public class WebPage {\r
+    public void show(OutputStreamWriter out) throws IOException {\r
+       Calendar c = new GregorianCalendar();\r
+       out.write(c.getTime().toString());\r
+       out.write("<P><A HREF=\"demo.html\">Return to the home page.</A>");\r
+    }\r
+}\r
+/*\r
+// WebPage.class.1\r
+public class WebPage {\r
+    public void show(OutputStreamWriter out) throws IOException {\r
+       out.write("<H2>Current Time:</H2>");\r
+       Calendar c = new GregorianCalendar();\r
+       out.write("<CENTER><H3><FONT color=\"blue\">");\r
+       out.write(c.getTime().toString());\r
+       out.write("</FONT></H3></CENTER><HR>");\r
+       out.write("<P><A HREF=\"demo.html\">Return to the home page.</A>");\r
+    }\r
+}\r
+*/\r
diff --git a/sample/evolve/demo.html b/sample/evolve/demo.html
new file mode 100644 (file)
index 0000000..6be4a2c
--- /dev/null
@@ -0,0 +1,38 @@
+<H2>Class Evolution</H2>\r
+\r
+<P>This is a demonstration of the class evolution mechanism\r
+implemented with Javassist.  This mechanism enables a Java program to\r
+reload an existing class file.  Although the reloaded class file is\r
+not applied to exiting objects (the old class file is used for those\r
+objects), it is effective in newly created objects.\r
+\r
+<P>Since the reloading is transparently executed, no programming\r
+convention is needed.  However, there are some limitations on possible\r
+changes of the class definition.  For example, the new class definition\r
+must have the same set of methods as the old one.  These limitations are\r
+necessary for keeping the type system consistent.\r
+\r
+\r
+<H3><a href="java.html">Run WebPage.show()</a></H3>\r
+\r
+<P>The web server creates a new <code>WebPage</code> object and\r
+calls <code>show()</code> on that object.  This method works as\r
+if it is a CGI script or a servlet and you will see the html file\r
+produced by this method on your browser.\r
+\r
+<H3><a href="update.html">Change WebPage.class</a></H3>\r
+\r
+<P>The web server overwrites class file <code>WebPage.class</code>\r
+on the local disk.  Then it signals that <code>WebPage.class</code>\r
+must be reloaded into the JVM.  If you run <code>WebPage.show()</code>\r
+again, you will see a different page on your browser.\r
+\r
+<H3>Source files</H3>\r
+\r
+<P>Web server: <A HREF="DemoServer.java"><code>DemoServer.java</code></A>\r
+\r
+<P>WebPage: <A HREF="WebPage.java"><code>WebPage.java</code></A>\r
+\r
+<P>Class loader: <A HREF="DemoLoader.java"><code>DemoLoader.java</code></A>,\r
+       <A HREF="Evolution.java"><code>Evolution.java</code></A>, and\r
+       <A HREF="VersionManager.java"><code>VersionManager.java</code></A>.\r
diff --git a/sample/evolve/start.html b/sample/evolve/start.html
new file mode 100644 (file)
index 0000000..d31d9d0
--- /dev/null
@@ -0,0 +1,23 @@
+<h2>Instructions</h2>\r
+\r
+<p>1. Compile <code>sample/evolve/*.java</code>.\r
+       Copy <code>WebPage.class</code> to <code>WebPage.class.0</code>.\r
+\r
+<p>2. Edit <code>Webpage.java</code>, compile it,\r
+       and copy <code>WebPage.class</code> to <code>WebPage.class.1</code>.\r
+<br><code>WebPage.class.0</code> and\r
+       <code>WebPage.class.1</code> are used\r
+       for changing the contents of <code>WebPage.class</code> during\r
+       the demo.\r
+\r
+<p>3. Run the server on the local host (where your web browser is running):\r
+\r
+<ul>\r
+<code>% java sample.evolve.DemoLoader 5003\r
+</code></ul>\r
+\r
+<p>4. Click below:\r
+\r
+<ul><h2><a href="http://localhost:5003/demo.html">\r
+Start!\r
+</a></h2></ul>\r
diff --git a/sample/evolve/update.html b/sample/evolve/update.html
new file mode 100644 (file)
index 0000000..8255165
--- /dev/null
@@ -0,0 +1,3 @@
+<h2><code>WebPage.class</code> has been changed.</h2>\r
+\r
+<a href="demo.html">Return to the home page.</a>\r
diff --git a/sample/reflect/Main.java b/sample/reflect/Main.java
new file mode 100644 (file)
index 0000000..6086d9f
--- /dev/null
@@ -0,0 +1,33 @@
+package sample.reflect;\r
+\r
+import javassist.reflect.ClassMetaobject;\r
+import javassist.reflect.Loader;\r
+\r
+/*\r
+  The "verbose metaobject" example (JDK 1.2 or later only).\r
+\r
+  Since this program registers class Person as a reflective class\r
+  (in a more realistic demonstration, what classes are reflective\r
+  would be specified by some configuration file), the class loader\r
+  modifies Person.class when loading into the JVM so that the class\r
+  Person is changed into a reflective class and a Person object is\r
+  controlled by a VerboseMetaobj.\r
+\r
+  To run,\r
+\r
+  % java javassist.reflect.Loader sample.reflect.Main Joe\r
+\r
+  Compare this result with that of the regular execution without reflection:\r
+\r
+  % java sample.reflect.Person Joe\r
+*/\r
+public class Main {\r
+    public static void main(String[] args) throws Throwable {\r
+       Loader cl = (Loader)Main.class.getClassLoader();\r
+       cl.makeReflective("sample.reflect.Person",\r
+                         "sample.reflect.VerboseMetaobj",\r
+                         "javassist.reflect.ClassMetaobject");\r
+\r
+       cl.run("sample.reflect.Person", args);\r
+    }\r
+}\r
diff --git a/sample/reflect/Person.java b/sample/reflect/Person.java
new file mode 100644 (file)
index 0000000..5e0e2ad
--- /dev/null
@@ -0,0 +1,51 @@
+/*\r
+  A base-level class controlled by VerboseMetaobj.\r
+*/\r
+\r
+package sample.reflect;\r
+\r
+import javassist.reflect.Metalevel;\r
+import javassist.reflect.Metaobject;\r
+\r
+public class Person {\r
+    public String name;\r
+    public static int birth = 3;\r
+    public static final String defaultName = "John";\r
+\r
+    public Person(String name, int birthYear) {\r
+       if (name == null)\r
+           this.name = defaultName;\r
+       else\r
+           this.name = name;\r
+\r
+       this.birth = birthYear;\r
+    }\r
+\r
+    public String getName() {\r
+       return name;\r
+    }\r
+\r
+    public int getAge(int year) {\r
+       return year - birth;\r
+    }\r
+\r
+    public static void main(String[] args) {\r
+       String name;\r
+       if (args.length > 0)\r
+           name = args[0];\r
+       else\r
+           name = "Bill";\r
+\r
+       Person p = new Person(name, 1960);\r
+       System.out.println("name: " + p.getName());\r
+       System.out.println("object: " + p.toString());\r
+\r
+       // change the metaobject of p.\r
+       if (p instanceof Metalevel) {\r
+           ((Metalevel)p)._setMetaobject(new Metaobject(p, null));\r
+           System.out.println("<< the metaobject was changed.>>");\r
+       }\r
+\r
+       System.out.println("age: " + p.getAge(1999));\r
+    }\r
+}\r
diff --git a/sample/reflect/VerboseMetaobj.java b/sample/reflect/VerboseMetaobj.java
new file mode 100644 (file)
index 0000000..a9f75dd
--- /dev/null
@@ -0,0 +1,29 @@
+package sample.reflect;\r
+\r
+import javassist.*;\r
+import javassist.reflect.*;\r
+\r
+public class VerboseMetaobj extends Metaobject {\r
+    public VerboseMetaobj(Object self, Object[] args) {\r
+       super(self, args);\r
+       System.out.println("** constructed: " + self.getClass().getName());\r
+    }\r
+\r
+    public Object trapFieldRead(String name) {\r
+       System.out.println("** field read: " + name);\r
+       return super.trapFieldRead(name);\r
+    }\r
+\r
+    public void trapFieldWrite(String name, Object value) {\r
+       System.out.println("** field write: " + name);\r
+       super.trapFieldWrite(name, value);\r
+    }\r
+\r
+    public Object trapMethodcall(int identifier, Object[] args)\r
+       throws Throwable\r
+    {\r
+       System.out.println("** trap: " + getMethodName(identifier)\r
+                          + "() in " + getClassMetaobject().getName());\r
+       return super.trapMethodcall(identifier, args);\r
+    }\r
+}\r
diff --git a/sample/rmi/AlertDialog.java b/sample/rmi/AlertDialog.java
new file mode 100644 (file)
index 0000000..99fae5c
--- /dev/null
@@ -0,0 +1,30 @@
+package sample.rmi;\r
+\r
+import java.awt.*;\r
+import java.awt.event.*;\r
+\r
+public class AlertDialog extends Frame implements ActionListener {\r
+    private Label label;\r
+\r
+    public AlertDialog() {\r
+       super("Alert");\r
+       setSize(200, 100);\r
+       setLocation(100, 100);\r
+       label = new Label();\r
+       Button b = new Button("OK");\r
+       b.addActionListener(this);\r
+       Panel p = new Panel();\r
+       p.add(b);\r
+       add("North", label);\r
+       add("South", p);\r
+    }\r
+\r
+    public void show(String message) {\r
+       label.setText(message);\r
+       setVisible(true);\r
+    }\r
+\r
+    public void actionPerformed(ActionEvent e) {\r
+       setVisible(false);\r
+    }\r
+}\r
diff --git a/sample/rmi/CountApplet.java b/sample/rmi/CountApplet.java
new file mode 100644 (file)
index 0000000..0bebdaf
--- /dev/null
@@ -0,0 +1,83 @@
+package sample.rmi;\r
+\r
+import java.applet.*;\r
+import java.awt.*;\r
+import java.awt.event.*;\r
+import javassist.rmi.ObjectImporter;\r
+import javassist.rmi.ObjectNotFoundException;\r
+import javassist.web.Viewer;\r
+\r
+public class CountApplet extends Applet implements ActionListener {\r
+    private Font font;\r
+    private ObjectImporter importer;\r
+    private Counter counter;\r
+    private AlertDialog dialog;\r
+    private String message;\r
+\r
+    private String paramButton;\r
+    private String paramName;\r
+\r
+    public void init() {\r
+       paramButton = getParameter("button");\r
+       paramName = getParameter("name");\r
+       importer = new ObjectImporter(this);\r
+       commonInit();\r
+    }\r
+\r
+    /* call this method instead of init() if this program is not run\r
+     * as an applet.\r
+     */\r
+    public void applicationInit() {\r
+       paramButton = "OK";\r
+       paramName = "counter";\r
+       Viewer cl = (Viewer)getClass().getClassLoader();\r
+       importer = new ObjectImporter(cl.getServer(), cl.getPort());\r
+       commonInit();\r
+    }\r
+\r
+    private void commonInit() {\r
+       font = new Font("SansSerif", Font.ITALIC, 40);\r
+       Button b = new Button(paramButton);\r
+       b.addActionListener(this);\r
+       add(b);\r
+       dialog = new AlertDialog();\r
+       message = "???";\r
+    }\r
+\r
+    public void destroy() {\r
+       dialog.dispose();\r
+    }\r
+\r
+    public void start() {\r
+       try {\r
+           counter = (Counter)importer.lookupObject(paramName);\r
+           message = Integer.toString(counter.get());\r
+       }\r
+       catch (ObjectNotFoundException e) {\r
+           dialog.show(e.toString());\r
+       }\r
+    }\r
+\r
+    public void actionPerformed(ActionEvent e) {\r
+       counter.increase();\r
+       message = Integer.toString(counter.get());\r
+       repaint();\r
+    }\r
+\r
+    public void paint(Graphics g) {\r
+       g.setFont(font);\r
+       g.drawRect(50, 50, 100, 100);\r
+       g.setColor(Color.blue);\r
+       g.drawString(message, 60, 120);\r
+    }\r
+\r
+    public static void main(String[] args) {\r
+       Frame f = new Frame("CountApplet");\r
+       CountApplet ca = new CountApplet();\r
+       f.add(ca);\r
+       f.setSize(300, 300);\r
+       ca.applicationInit();\r
+       ca.start();\r
+       f.setVisible(true);\r
+    }\r
+}\r
diff --git a/sample/rmi/Counter.java b/sample/rmi/Counter.java
new file mode 100644 (file)
index 0000000..f8a0fcf
--- /dev/null
@@ -0,0 +1,32 @@
+package sample.rmi;\r
+\r
+import javassist.rmi.AppletServer;\r
+import java.io.IOException;\r
+import javassist.CannotCompileException;\r
+import javassist.NotFoundException;\r
+\r
+public class Counter {\r
+    private int count = 0;\r
+\r
+    public int get() {\r
+       return count;\r
+    }\r
+\r
+    synchronized public int increase() {\r
+       count += 1;\r
+       return count;\r
+    }\r
+\r
+    public static void main(String[] args)\r
+       throws IOException, NotFoundException, CannotCompileException\r
+    {\r
+       if (args.length == 1) {\r
+           AppletServer web = new AppletServer(args[0]);\r
+           web.exportObject("counter", new Counter());\r
+           web.run();\r
+       }\r
+       else\r
+           System.err.println(\r
+                       "Usage: java sample.rmi.Counter <port number>");\r
+    }\r
+}\r
diff --git a/sample/rmi/inside.gif b/sample/rmi/inside.gif
new file mode 100644 (file)
index 0000000..c69c8ee
Binary files /dev/null and b/sample/rmi/inside.gif differ
diff --git a/sample/rmi/start.html b/sample/rmi/start.html
new file mode 100644 (file)
index 0000000..696b629
--- /dev/null
@@ -0,0 +1,15 @@
+<h2>Instructions</h2>\r
+\r
+<p>1. Run the server on the local host (where your web browser is running):\r
+\r
+<ul>% java sample.rmi.Counter 5001</ul>\r
+\r
+<p>2. Click below:\r
+\r
+<h2><a href="webdemo.html">\r
+Start!\r
+</a></h2>\r
+\r
+<p>If you don't want to use a web browser, do as follows:\r
+\r
+<ul><pre>% java javassist.web.Viewer localhost 5001 sample.rmi.CountApplet</pre></ul>\r
diff --git a/sample/rmi/webdemo.html b/sample/rmi/webdemo.html
new file mode 100644 (file)
index 0000000..d313ec3
--- /dev/null
@@ -0,0 +1,203 @@
+<html>\r
+<body>\r
+<h2>Remote Method Invocation</h2>\r
+\r
+<P>Javassist enables an applet to access a remote object as if it is a\r
+local object.  The applet can communicate through a socket with the\r
+host that executes the web server distributing that applet.  However,\r
+the applet cannot directly call a method on an object if the object is\r
+on a remote host.  The <code>javassist.rmi</code> package provides\r
+a mechanism for the applet to transparently access the remote object.\r
+The rules that the applet must be subject to are simpler than the\r
+standard Java RMI.\r
+\r
+<h3>1. Sample applet</h3>\r
+\r
+<P>The applet showing below is a simple number counter.\r
+If you press the button, the number is increased by one.\r
+An interesting feature of this applet is that the object\r
+recording the current number is contained by the web server\r
+written in Java.  The applet must access the object through a socket\r
+to obtain the current number.\r
+\r
+<p><center>\r
+<applet codebase="http://localhost:5001"\r
+code="sample.rmi.CountApplet" width=200 height=200>\r
+<param name=name value="counter">\r
+<param name=button value="+1">\r
+</applet>\r
+</center>\r
+\r
+<p>However, the program of the applet does not need to directly handle\r
+a socket.  The <code>ObjectImporter</code> provided by Javassist deals\r
+with all the awkward programming.\r
+Look at the lines shown with red:\r
+\r
+<p><b>Figure 1: Applet</b>\r
+\r
+<pre>\r
+<font color="red">import javassist.rmi.ObjectImporter;</font>\r
+\r
+public class CountApplet extends Applet implements ActionListener {\r
+  private Font font;\r
+  <font color="red">private ObjectImporter importer;\r
+  private Counter counter;</font>\r
+  private AlertDialog dialog;\r
+  private String message;\r
+\r
+  public void init() {\r
+    font = new Font("SansSerif", Font.ITALIC, 40);\r
+    Button b = new Button(getParameter("button"));\r
+    b.addActionListener(this);\r
+    add(b);\r
+    <font color="red">importer = new ObjectImporter(this);</font>\r
+    dialog = new AlertDialog();\r
+    message = "???";\r
+  }\r
+\r
+  public void start() {\r
+    String counterName = getParameter("name");\r
+    <font color="red">counter = (Counter)importer.getObject(counterName);</font>\r
+    message = Integer.toString(<font color="red">counter.get()</font>);\r
+  }\r
+\r
+  /* The method called when the button is pressed.\r
+  */\r
+  public void actionPerformed(ActionEvent e) {\r
+    message = Integer.toString(<font color="red">counter.increase()</font>);\r
+    repaint();\r
+  }\r
+\r
+  public void paint(Graphics g) {\r
+    g.setFont(font);\r
+    g.drawRect(50, 50, 100, 100);\r
+    g.setColor(Color.blue);\r
+    g.drawString(message, 60, 120);\r
+  }\r
+}\r
+</pre>\r
+\r
+<p>A <code>Counter</code> object running on a remote host\r
+maintains the counter number.  To access this object, the applet first\r
+calls <code>getObject()</code> on an <code>ObjectImporter</code>\r
+to obtain a reference to the object.  The parameter is the name associated\r
+with the object by the web server.  Once the reference is obtained, it\r
+is delt with as if it is a reference to a local object.\r
+For example, <code>counter.get()</code> and <code>counter.increase()</code>\r
+call methods on the remote object.\r
+\r
+<p>The definition of the <code>Counter</code> class is also\r
+straightforward:\r
+\r
+<p><b>Figure 2: Remote object</b>\r
+\r
+<pre>\r
+public class Counter {\r
+  private int count = 0;\r
+\r
+  public int get() {\r
+    return count;\r
+  }\r
+\r
+  public int increase() {\r
+    count += 1;\r
+    return count;\r
+  }\r
+}\r
+</pre>\r
+\r
+<p>Note that the <code>javassist.rmi</code> package does not require\r
+the <code>Counter</code> class to be an interface unlike the Java RMI,\r
+with which <code>Counter</code> must be an interface and it must be\r
+implemented by another class.\r
+\r
+<p>To make the <code>Counter</code> object available from the applet,\r
+it must be registered with the web server.  A <code>AppletServer</code>\r
+object is a simple webserver that can distribute <code>.html</code> files\r
+and <code>.class</code> files (Java applets).\r
+\r
+<p><b>Figure 3: Server-side program</b>\r
+\r
+<pre>\r
+public class MyWebServer {\r
+  public static void main(String[] args) throws IOException, CannotCompileException\r
+  {\r
+      AppletServer web = new AppletServer(args[0]);\r
+      <font color="red">web.exportObject("counter", new Counter());</font>\r
+      web.run();\r
+  }\r
+}\r
+</pre>\r
+\r
+<p>The <code>exportObject()</code> method registers a remote object\r
+with the <code>AppletServer</code> object.  In the example above,\r
+a <code>Counter</code> object is registered.  The applet can access\r
+the object with the name "counter".  The web server starts the service\r
+if the <code>run()</code> method is called.\r
+\r
+<p><br>\r
+\r
+<h3>2. Features</h3>\r
+\r
+The remote method invocation mechanism provided by Javassist has the\r
+following features:\r
+\r
+<ul>\r
+<li><b>Regular Java syntax:</b><br>\r
+       The applet can call a method on a remote object with regular\r
+       Java syntax.\r
+<p>\r
+\r
+<li><b>No special naming convention:</b><br>\r
+       The applet can use the same class name as the server-side program.\r
+       The reference object to a remote <code>Foo</code> object is\r
+       also represented by the class <code>Foo</code>.\r
+       Unlike other similar\r
+       systems, it is not represented by a different class such as\r
+       <code>ProxyFoo</code> or an interface implemented by\r
+       <code>Foo</code>.\r
+<p>\r
+\r
+<li><b>No extra compiler:</b><br>\r
+       All the programs, both the applet and the server-side program,\r
+       are compiled by the regular Java compiler.  No external compiler\r
+       is needed.\r
+</ul>\r
+\r
+<p> With the Java RMI or Voyager, the applet programmer must define\r
+an interface for every remote object class and access the remote object\r
+through that interface.\r
+On the other hand, the <code>javassist.rmi</code> package does not\r
+require the programmer to follow that programming convention.\r
+It is suitable for writing simple distributed programs like applets.\r
+\r
+<p><br>\r
+\r
+<h3>3. Inside of the system</h3>\r
+\r
+<p>A key idea of the implementation is that the applet and the server-side\r
+program must use different versions of the class <code>Counter</code>.\r
+The <code>Counter</code> object in the applet must work as a proxy\r
+object, which transfers the method invocations to the <code>Counter</code>\r
+object in the server-side program.\r
+\r
+<p>With other systems like the Java RMI, the class of this proxy object is\r
+produced by a special compiler such as <code>rmic</code>.\r
+It must be manually maintained by the programmer.\r
+\r
+<center><img src="inside.gif"></center>\r
+\r
+<p>However, Javassist automatically generates the proxy class at\r
+runtime so that the programmer does not have to be concerned about the\r
+maintenance of the proxy class.\r
+If the web browser running the applet\r
+requests to load the <code>Counter</code> class, which is the class\r
+of an exported object,\r
+then the web server\r
+transfers the version of <code>Counter</code> that Javassist generates\r
+as a proxy class.\r
+\r
+<p><br>\r
+\r
+</body>\r
+</html>\r
diff --git a/sample/vector/Sample.java b/sample/vector/Sample.java
new file mode 100644 (file)
index 0000000..7a47aad
--- /dev/null
@@ -0,0 +1,14 @@
+package sample.vector;\r
+\r
+public class Sample extends java.util.Vector {\r
+    public void add(X e) {\r
+       super.addElement(e);\r
+    }\r
+\r
+    public X at(int i) {\r
+       return (X)super.elementAt(i);\r
+    }\r
+}\r
+\r
+class X {\r
+}\r
diff --git a/sample/vector/Sample2.java b/sample/vector/Sample2.java
new file mode 100644 (file)
index 0000000..dd5c965
--- /dev/null
@@ -0,0 +1,13 @@
+package sample.vector;\r
+\r
+public class Sample2 extends java.util.Vector {\r
+    public Object add(Object[] args) {\r
+       super.addElement(args[0]);\r
+       return null;\r
+    }\r
+\r
+    public Object at(Object[] args) {\r
+       int i = ((Integer)args[0]).intValue();\r
+       return super.elementAt(i);\r
+    }\r
+}\r
diff --git a/sample/vector/Test.j b/sample/vector/Test.j
new file mode 100644 (file)
index 0000000..6f524c9
--- /dev/null
@@ -0,0 +1,38 @@
+/*\r
+  A sample program using sample.vector.VectorAssistant\r
+  and the javassist.preproc package.\r
+\r
+  This automatically produces the classes representing vectors of integer\r
+  and vectors of java.lang.String.\r
+\r
+  To compile and run this program, do as follows:\r
+\r
+    % java javassist.tool.Compiler sample/vector/Test.j\r
+    % javac sample/vector/Test.java\r
+    % java sample.vector.Test\r
+\r
+  The first line produces one source file (sample/Test.java) and\r
+  two class files (sample/vector/intVector.class and\r
+  sample/vector/StringVector.class).\r
+*/\r
+\r
+package sample.vector;\r
+\r
+import java.util.Vector by sample.vector.VectorAssistant(java.lang.String);\r
+import java.util.Vector by sample.vector.VectorAssistant(int);\r
+\r
+public class Test {\r
+    public static void main(String[] args) {\r
+       intVector iv = new intVector();\r
+       iv.add(3);\r
+       iv.add(4);\r
+       for (int i = 0; i < iv.size(); ++i)\r
+           System.out.println(iv.at(i));\r
+\r
+       StringVector sv = new StringVector();\r
+       sv.add("foo");\r
+       sv.add("bar");\r
+       for (int i = 0; i < sv.size(); ++i)\r
+           System.out.println(sv.at(i));\r
+    }\r
+}\r
diff --git a/sample/vector/VectorAssistant.java b/sample/vector/VectorAssistant.java
new file mode 100644 (file)
index 0000000..44fdd41
--- /dev/null
@@ -0,0 +1,135 @@
+package sample.vector;\r
+\r
+import java.io.IOException;\r
+import javassist.*;\r
+import javassist.preproc.Assistant;\r
+\r
+/**\r
+ * This is a Javassist program which produce a new class representing\r
+ * vectors of a given type.  For example,\r
+ *\r
+ * <ul>import java.util.Vector by sample.vector.VectorAssistant(int)</ul>\r
+ *\r
+ * <p>requests the Javassist preprocessor to substitute the following\r
+ * lines for the original import declaration:\r
+ *\r
+ * <ul><pre>\r
+ * import java.util.Vector;\r
+ * import sample.vector.intVector;\r
+ * </pre></ul>\r
+ *\r
+ * <p>The Javassist preprocessor calls <code>VectorAssistant.assist()</code>\r
+ * and produces class <code>intVector</code> equivalent to:\r
+ *\r
+ * <ul><pre>\r
+ * package sample.vector;\r
+ *\r
+ * public class intVector extends Vector {\r
+ *   pubilc void add(int value) {\r
+ *     addElement(new Integer(value));\r
+ *   }\r
+ *\r
+ *   public int at(int index) {\r
+ *     return elementAt(index).intValue();\r
+ *   }\r
+ * }\r
+ * </pre></ul>\r
+ *\r
+ * <p><code>VectorAssistant.assist()</code> uses\r
+ * <code>sample.vector.Sample</code> and <code>sample.vector.Sample2</code>\r
+ * as a template to produce the methods <code>add()</code> and\r
+ * <code>at()</code>.\r
+ */\r
+public class VectorAssistant implements Assistant {\r
+    public final String packageName = "sample.vector.";\r
+\r
+    /**\r
+     * Calls <code>makeSubclass()</code> and produces a new vector class.\r
+     * This method is called by a <code>javassist.preproc.Compiler</code>.\r
+     *\r
+     * @see javassist.preproc.Compiler\r
+     */\r
+    public CtClass[] assist(ClassPool pool, String vec, String[] args)\r
+       throws CannotCompileException\r
+    {\r
+       if (args.length != 1)\r
+           throw new CannotCompileException(\r
+                       "VectorAssistant receives a single argument.");\r
+\r
+       try {\r
+           CtClass subclass;\r
+           CtClass elementType = pool.get(args[0]);\r
+           if (elementType.isPrimitive())\r
+               subclass = makeSubclass2(pool, elementType);\r
+           else\r
+               subclass = makeSubclass(pool, elementType);\r
+\r
+           CtClass[] results = { subclass, pool.get(vec) };\r
+           return results;\r
+       }\r
+       catch (NotFoundException e) {\r
+           throw new CannotCompileException(e);\r
+       }\r
+       catch (IOException e) {\r
+           throw new CannotCompileException(e);\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Produces a new vector class.  This method does not work if\r
+     * the element type is a primitive type.\r
+     *\r
+     * @param type     the type of elements\r
+     */\r
+    public CtClass makeSubclass(ClassPool pool, CtClass type)\r
+       throws CannotCompileException, NotFoundException, IOException\r
+    {\r
+       CtClass vec = pool.makeClass(makeClassName(type));\r
+       vec.setSuperclass(pool.get("java.util.Vector"));\r
+\r
+       CtClass c = pool.get("sample.vector.Sample");\r
+       CtMethod addmethod = c.getDeclaredMethod("add");\r
+       CtMethod atmethod = c.getDeclaredMethod("at");\r
+\r
+       ClassMap map = new ClassMap();\r
+       map.put("sample.vector.X", type.getName());\r
+\r
+       vec.addMethod(CtNewMethod.copy(addmethod, "add", vec, map));\r
+       vec.addMethod(CtNewMethod.copy(atmethod, "at", vec, map));\r
+       pool.writeFile(vec.getName());\r
+       return vec;\r
+    }\r
+\r
+    /**\r
+     * Produces a new vector class.  This uses wrapped methods so that\r
+     * the element type can be a primitive type.\r
+     *\r
+     * @param type     the type of elements\r
+     */\r
+    public CtClass makeSubclass2(ClassPool pool, CtClass type)\r
+       throws CannotCompileException, NotFoundException, IOException\r
+    {\r
+       CtClass vec = pool.makeClass(makeClassName(type));\r
+       vec.setSuperclass(pool.get("java.util.Vector"));\r
+\r
+       CtClass c = pool.get("sample.vector.Sample2");\r
+       CtMethod addmethod = c.getDeclaredMethod("add");\r
+       CtMethod atmethod = c.getDeclaredMethod("at");\r
+\r
+       CtClass[] args1 = { type };\r
+       CtClass[] args2 = { CtClass.intType };\r
+       CtMethod m\r
+           = CtNewMethod.wrapped(CtClass.voidType, "add", args1,\r
+                                 null, addmethod, null, vec);\r
+       vec.addMethod(m);\r
+       m = CtNewMethod.wrapped(type, "at", args2,\r
+                               null, atmethod, null, vec);\r
+       vec.addMethod(m);\r
+       pool.writeFile(vec.getName());\r
+       return vec;\r
+    }\r
+\r
+    private String makeClassName(CtClass type) {\r
+       return packageName + type.getSimpleName() + "Vector";\r
+    }\r
+}\r
diff --git a/src/main/META-INF/MANIFEST.MF b/src/main/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..b28ad33
--- /dev/null
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0\r
+Specification-Title: Javassist\r
+Created-By: Shigeru Chiba, Tokyo Institute of Technology\r
+Specification-Vendor: Shigeru Chiba, Tokyo Institute of Technology\r
+Specification-Version: 2.4\r
+Name: javassist/\r
+\r
diff --git a/src/main/javassist/ByteArrayClassPath.java b/src/main/javassist/ByteArrayClassPath.java
new file mode 100644 (file)
index 0000000..14b37d8
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.*;
+
+/**
+ * A <code>ByteArrayClassPath</code> contains bytes that is served as
+ * a class file to a <code>ClassPool</code>.  It is useful to convert
+ * a byte array to a <code>CtClass</code> object.
+ *
+ * <p>For example, if you want to convert a byte array <code>b</code>
+ * into a <code>CtClass</code> object representing the class with a name
+ * <code>classname</code>, then do as following:
+ *
+ * <ul><pre>
+ * ClassPool cp = ClassPool.getDefault();
+ * cp.insertClassPath(new ByteArrayClassPath(classname, b));
+ * CtClass cc = cp.get(classname);
+ * </pre></ul>
+ *
+ * <p>The <code>ClassPool</code> object <code>cp</code> uses the created
+ * <code>ByteArrayClassPath</code> object as the source of the class file.
+ * 
+ * <p>A <code>ByteArrayClassPath</code> must be instantiated for every
+ * class.  It contains only a single class file.
+ *
+ * @see javassist.ClassPath
+ * @see ClassPool#insertClassPath(ClassPath)
+ * @see ClassPool#appendClassPath(ClassPath)
+ */
+public class ByteArrayClassPath implements ClassPath {
+    protected String classname;
+    protected byte[] classfile;
+
+    /*
+     * Creates a <code>ByteArrayClassPath</code> containing the given
+     * bytes.
+     *
+     * @param name             a fully qualified class name
+     * @param classfile                the contents of a class file.
+     */
+    public ByteArrayClassPath(String name, byte[] classfile) {
+       this.classname = name;
+       this.classfile = classfile;
+    }
+
+    public String toString() {
+       return "byte[]:" + classname;
+    }
+
+    /**
+     * Opens a class file.
+     */
+    public InputStream openClassfile(String classname) {
+       if(this.classname.equals(classname))
+           return new ByteArrayInputStream(classfile);
+       else
+           return null;
+    }
+}
diff --git a/src/main/javassist/CannotCompileException.java b/src/main/javassist/CannotCompileException.java
new file mode 100644 (file)
index 0000000..41ec7ff
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Thrown when bytecode transformation has failed.
+ */
+public class CannotCompileException extends Exception {
+    private String message;
+
+    public String getReason() {
+       if (message != null)
+           return message;
+       else
+           return this.toString();
+    }
+
+    /**
+     * Constructs a CannotCompileException with a message.
+     */
+    public CannotCompileException(String msg) {
+       super(msg);
+       message = msg;
+    }
+
+    /**
+     * Constructs a CannotCompileException with an <code>Exception</code>.
+     */
+    public CannotCompileException(Exception e) {
+       super("by " + e.toString());
+       message = null;
+    }
+
+    /**
+     * Constructs a CannotCompileException with a
+     * <code>NotFoundException</code>.
+     */
+    public CannotCompileException(NotFoundException e) {
+       this("cannot find " + e.getMessage());
+    }
+
+    /**
+     * Constructs a CannotCompileException with an <code>CompileError</code>.
+     */
+    public CannotCompileException(CompileError e) {
+       super("[source error] " + e.getMessage());
+       message = null;
+    }
+
+    /**
+     * Constructs a CannotCompileException
+     * with a <code>ClassNotFoundException</code>.
+     */
+    public CannotCompileException(ClassNotFoundException e, String name) {
+       this("cannot find " + name);
+    }
+
+    /**
+     * Constructs a CannotCompileException with a ClassFormatError.
+     */
+    public CannotCompileException(ClassFormatError e, String name) {
+       this("invalid class format: " + name);
+    }
+}
diff --git a/src/main/javassist/ClassMap.java b/src/main/javassist/ClassMap.java
new file mode 100644 (file)
index 0000000..c729ff7
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.Descriptor;
+
+/**
+ * A hashtable associating class names with different names.
+ *
+ * <p>This hashtable is used for replacing class names in a class
+ * definition or a method body.  Define a subclass of this class
+ * if a more complex mapping algorithm is needed.  For example,
+ *
+ * <ul><pre>class MyClassMap extends ClassMap {
+ *   public Object get(Object jvmClassName) {
+ *     String name = toJavaName((String)jvmClassName);
+ *     if (name.startsWith("java."))
+ *         return toJvmName("java2." + name.substring(5));
+ *     else
+ *         return super.get(jvmClassName);
+ *   }
+ * }</pre></ul>
+ *
+ * <p>This subclass maps <code>java.lang.String</code> to
+ * <code>java2.lang.String</code>.  Note that <code>get()</code>
+ * receives and returns the internal representation of a class name.
+ * For example, the internal representation of <code>java.lang.String</code>
+ * is <code>java/lang/String</code>.
+ *
+ * @see #get(Object)
+ * @see CtClass#replaceClassName(ClassMap)
+ * @see CtNewMethod#copy(CtMethod,String,CtClass,ClassMap)
+ */
+public class ClassMap extends java.util.HashMap {
+    /**
+     * Maps a class name to another name in this hashtable.
+     * The names are obtained with calling <code>Class.getName()</code>.
+     * This method translates the given class names into the
+     * internal form used in the JVM before putting it in
+     * the hashtable.
+     *
+     * @param oldname  the original class name
+     * @param newname  the substituted class name.
+     */
+    public void put(CtClass oldname, CtClass newname) {
+       put(oldname.getName(), newname.getName());
+    }
+
+    /**
+     * Maps a class name to another name in this hashtable.
+     * This method translates the given class names into the
+     * internal form used in the JVM before putting it in
+     * the hashtable.
+     *
+     * @param oldname  the original class name
+     * @param newname  the substituted class name.
+     */
+    public void put(String oldname, String newname) {
+       if (oldname == newname)
+           return;
+
+       String oldname2 = toJvmName(oldname);
+       String s = (String)get(oldname2);
+       if (s == null || !s.equals(oldname2))
+           super.put(oldname2, toJvmName(newname));
+    }
+
+    protected final void put0(Object oldname, Object newname) {
+       super.put(oldname, newname);
+    }
+
+    /**
+     * Returns the class name to wihch the given <code>jvmClassName</code>
+     * is mapped.  A subclass of this class should override this method.
+     *
+     * <p>This method receives and returns the internal representation of
+     * class name used in the JVM.
+     *
+     * @see #toJvmName(String)
+     * @see #toJavaName(String)
+     */
+    public Object get(Object jvmClassName) {
+       return super.get(jvmClassName);
+    }
+
+    /**
+     * Prevents a mapping from the specified class name to another name.
+     */
+    public void fix(CtClass clazz) {
+       fix(clazz.getName());
+    }
+
+    /**
+     * Prevents a mapping from the specified class name to another name.
+     */
+    public void fix(String name) {
+       String name2 = toJvmName(name);
+       super.put(name2, name2);
+    }
+
+    /**
+     * Converts a class name into the internal representation used in
+     * the JVM.
+     */
+    public static String toJvmName(String classname) {
+       return Descriptor.toJvmName(classname);
+    }
+
+    /**
+     * Converts a class name from the internal representation used in
+     * the JVM to the normal one used in Java.
+     */
+    public static String toJavaName(String classname) {
+       return Descriptor.toJavaName(classname);
+    }
+}
diff --git a/src/main/javassist/ClassPath.java b/src/main/javassist/ClassPath.java
new file mode 100644 (file)
index 0000000..f50c225
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.InputStream;
+
+/**
+ * <code>ClassPath</code> is an interface implemented by objects
+ * representing a class search path.
+ * <code>ClassPool</code> uses those objects for reading class files.
+ *
+ * <code>The users can define a class implementing this interface so that
+ * a class file is obtained from a non-standard source.
+ *
+ * @see ClassPool#insertClassPath(ClassPath)
+ * @see ClassPool#appendClassPath(ClassPath)
+ * @see javassist.URLClassPath
+ */
+public interface ClassPath {
+    /**
+     * Opens a class file.
+     *
+     * <p>This method can return null if the specified class file is not
+     * found.  If null is returned, the next search path is examined.
+     * However, if an error happens, this method must throw an exception 
+     * so that the search is terminated.
+     *
+     * <p>This method should not modify the contents of the class file.
+     *
+     * @param classname                a fully-qualified class name
+     * @return         the input stream for reading a class file
+     */
+    InputStream openClassfile(String classname) throws NotFoundException;
+}
diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java
new file mode 100644 (file)
index 0000000..3390744
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.*;
+import java.util.Hashtable;
+
+/**
+ * A driver class for controlling bytecode editing with Javassist.
+ * It manages where a class file is obtained and how it is modified.
+ *
+ * <p>A <code>ClassPool</code> object can be regarded as a container
+ * of <code>CtClass</code> objects.  It reads class files on demand
+ * from various
+ * sources represented by <code>ClassPath</code> and create
+ * <code>CtClass</code> objects representing those class files.
+ * The source may be another <code>ClassPool</code>.  If so,
+ * <code>write()</code> is called on the source <code>ClassPool</code>
+ * for obtaining a class file.
+ *
+ * <p>A <code>CtClass</code>
+ * object contained in a <code>ClassPool</code> is written to an
+ * output stream (or a file) if <code>write()</code>
+ * (or <code>writeFile()</code>) is called on the 
+ * <code>ClassPool</code>.
+ * <code>write()</code> is typically called by a class loader,
+ * which obtains the bytecode image to be loaded.
+ *
+ * <p>The users can modify <code>CtClass</code> objects
+ * before those objects are written out.
+ * To obtain a reference
+ * to a <code>CtClass</code> object contained in a
+ * <code>ClassPool</code>, <code>get()</code> should be
+ * called on the <code>ClassPool</code>.  If a <code>CtClass</code>
+ * object is modified, then the modification is reflected on the resulting
+ * class file returned by <code>write()</code> in <code>ClassPool</code>.
+ *
+ * <p>In summary,
+ *
+ * <ul>
+ * <li><code>get()</code> returns a reference to a <code>CtClass</code>
+ *     object contained in a <code>ClassPool</code>.
+ *
+ * <li><code>write()</code> translates a <code>CtClass</code>
+ * object contained in a <code>ClassPool</code> into a class file
+ * and writes it to an output stream.
+ * </ul>
+ *
+ * <p>The users can add a listener object receiving an event from a
+ * <code>ClassPool</code>.  An event occurs when a listener is
+ * added to a <code>ClassPool</code> and when <code>write()</code>
+ * is called on a <code>ClassPool</code>.  The listener class
+ * must implement <code>Translator</code>.  A typical listener object
+ * is used for modifying a <code>CtClass</code> object <i>on demand</i>
+ * when it is written to an output stream.
+ *
+ * <p>The implementation of this class is thread-safe.
+ *
+ * @see javassist.CtClass
+ * @see javassist.ClassPath
+ * @see javassist.Translator
+ */
+public class ClassPool {
+    /* If this field is null, then the object must be an instance of
+     * ClassPoolTail.
+     */
+    protected ClassPool source;
+
+    protected Translator translator;
+
+    protected Hashtable classes;       // should be synchronous
+
+    /**
+     * Creates a class pool.
+     *
+     * @param src      the source of class files.  If it is null,
+     *                 the class search path is initially null.
+     * @see javassist.ClassPool#getDefault()
+     */
+    public ClassPool(ClassPool src) {
+       this(src, null);
+    }
+
+    /**
+     * Creates a class pool.
+     *
+     * @param src      the source of class files.  If it is null,
+     *                 the class search path is initially null.
+     * @param trans    the translator linked to this class pool.
+     *                 It may be null.
+     * @see javassist.ClassPool#getDefault()
+     */
+    public ClassPool(ClassPool src, Translator trans)
+       throws RuntimeException
+    {
+       classes = new Hashtable();
+       CtClass[] pt = CtClass.primitiveTypes;
+       for (int i = 0; i < pt.length; ++i)
+           classes.put(pt[i].getName(), pt[i]);
+
+       if (src != null)
+           source = src;
+       else
+           source = new ClassPoolTail();
+
+       translator = trans;
+       if (trans != null)
+           try {
+               trans.start(this);
+           }
+           catch (Exception e) {
+               throw new RuntimeException(
+                   "Translator.start() throws an exception: "
+                   + e.toString());
+           }
+    }
+
+    protected ClassPool() {
+       source = null;
+       classes = null;
+       translator = null;
+    }
+
+    /**
+     * Returns the default class pool.
+     * The returned object is always identical.
+     *
+     * <p>The default class pool searches the system search path,
+     * which usually includes the platform library, extension
+     * libraries, and the search path specified by the
+     * <code>-classpath</code> option or the <code>CLASSPATH</code>
+     * environment variable.
+     *
+     * @param t                null or the translator linked to the class pool.
+     */
+    public static synchronized ClassPool getDefault(Translator t) {
+       if (defaultPool == null) {
+           ClassPoolTail tail = new ClassPoolTail();
+           tail.appendSystemPath();
+           defaultPool = new ClassPool(tail, t);
+       }
+
+       return defaultPool;
+    }
+
+    private static ClassPool defaultPool = null;
+
+    /**
+     * Returns the default class pool.
+     * The returned object is always identical.
+     *
+     * <p>This returns the result of <code>getDefault(null)</code>.
+     *
+     * @see #getDefault(Translator)
+     */
+    public static ClassPool getDefault() {
+       return getDefault(null);
+    }
+
+    /**
+     * Returns the class search path.
+     */
+    public String toString() {
+       return source.toString();
+    }
+
+    /**
+     * Returns the <code>Translator</code> object associated with
+     * this <code>ClassPool</code>.
+     */
+    public Translator getTranslator() { return translator; }
+
+    /**
+     * Table of registered cflow variables.
+     */
+    private Hashtable cflow = null;    // should be synchronous.
+
+    /**
+     * Records the <code>$cflow</code> variable for the field specified
+     * by <code>cname</code> and <code>fname</code>.
+     *
+     * @param name     variable name
+     * @param cname    class name
+     * @param fname    field name
+     */
+    void recordCflow(String name, String cname, String fname) {
+       if (cflow == null)
+           cflow = new Hashtable();
+
+       cflow.put(name, new Object[] { cname, fname });
+    }
+
+    /**
+     * Undocumented method.  Do not use; internal-use only.
+     * 
+     * @param name     the name of <code>$cflow</code> variable
+     */
+    public Object[] lookupCflow(String name) {
+       if (cflow == null)
+           cflow = new Hashtable();
+
+       return (Object[])cflow.get(name);
+    }
+
+    /**
+     * Writes a class file specified with <code>classname</code>
+     * in the current directory.
+     * It never calls <code>onWrite()</code> on a translator.
+     * It is provided for debugging.
+     *
+     * @param classname                the name of the class written on a local disk.
+     */
+    public void debugWriteFile(String classname)
+       throws NotFoundException, CannotCompileException, IOException
+    {
+       debugWriteFile(classname, ".");
+    }
+
+    /**
+     * Writes a class file specified with <code>classname</code>.
+     * It never calls <code>onWrite()</code> on a translator.
+     * It is provided for debugging.
+     *
+     * @param classname                the name of the class written on a local disk.
+     * @param directoryName    it must end without a directory separator.
+     */
+    public void debugWriteFile(String classname, String directoryName)
+       throws NotFoundException, CannotCompileException, IOException
+    {
+       writeFile(classname, directoryName, false);
+    }
+
+    /* void writeFile(CtClass) should not be defined since writeFile()
+     * may be called on the class pool that does not contain the given
+     * CtClass object.
+     */
+
+    /**
+     * Writes a class file specified with <code>classname</code>
+     * in the current directory.
+     * It calls <code>onWrite()</code> on a translator.
+     *
+     * @param classname                the name of the class written on a local disk.
+     */
+    public void writeFile(String classname)
+       throws NotFoundException, CannotCompileException, IOException
+    {
+       writeFile(classname, ".");
+    }
+
+    /**
+     * Writes a class file specified with <code>classname</code>
+     * on a local disk.
+     * It calls <code>onWrite()</code> on a translator.
+     *
+     * @param classname                the name of the class written on a local disk.
+     * @param directoryName    it must end without a directory separator.
+     */
+    public void writeFile(String classname, String directoryName)
+       throws NotFoundException, CannotCompileException, IOException
+    {
+       writeFile(classname, directoryName, true);
+    }
+
+    private void writeFile(String classname, String directoryName,
+                          boolean callback)
+       throws NotFoundException, CannotCompileException, IOException
+    {
+       String filename = directoryName + File.separatorChar
+           + classname.replace('.', File.separatorChar) + ".class";
+       int pos = filename.lastIndexOf(File.separatorChar);
+       if (pos > 0) {
+           String dir = filename.substring(0, pos);
+           if (!dir.equals("."))
+               new File(dir).mkdirs();
+       }
+
+       DataOutputStream out
+           = new DataOutputStream(new BufferedOutputStream(
+                               new DelayedFileOutputStream(filename)));
+       write(classname, out, callback);
+       out.close();
+    }
+
+    static class DelayedFileOutputStream extends OutputStream {
+       private FileOutputStream file;
+       private String filename;
+
+       DelayedFileOutputStream(String name) {
+           file = null;
+           filename = name;
+       }
+
+       private void init() throws IOException {
+           if (file == null)
+               file = new FileOutputStream(filename);
+       }
+
+       public void write(int b) throws IOException {
+           init();
+           file.write(b);
+       }
+
+       public void write(byte[] b) throws IOException {
+           init();
+           file.write(b);
+       }
+
+       public void write(byte[] b, int off, int len) throws IOException {
+           init();
+           file.write(b, off, len);
+
+       }
+
+       public void flush() throws IOException {
+           init();
+           file.flush();
+       }
+
+       public void close() throws IOException {
+           init();
+           file.close();
+       }
+    }
+
+    static class LocalClassLoader extends ClassLoader {
+       public Class loadClass(String name, byte[] classfile)
+           throws ClassFormatError
+       {
+           Class c = defineClass(name, classfile, 0, classfile.length);
+           resolveClass(c);
+           return c;
+       }
+    };
+
+    private static LocalClassLoader classLoader = new LocalClassLoader();
+
+    /**
+     * Returns a <code>java.lang.Class</code> object that has been loaded
+     * by <code>writeAsClass()</code>.  Note that such a class cannot be
+     * obtained by <code>java.lang.Class.forName()</code> because it has
+     * been loaded by an internal class loader.
+     *
+     * @see #writeAsClass(String)
+     * @see javassist.CtClass#toClass()
+     */
+    public static Class forName(String name) throws ClassNotFoundException {
+       return classLoader.loadClass(name);
+    }
+
+    /**
+     * Returns a <code>java.lang.Class</code> object.
+     * It calls <code>write()</code> to obtain a class file and then
+     * loads the obtained class file into the JVM.  The returned
+     * <code>Class</code> object represents the loaded class.
+     *
+     * <p>This method is provided for convenience.  If you need more
+     * complex functionality, you should write your own class loader.
+     *
+     * <p>To load a class file, this method uses an internal class loader.
+     * Thus, that class file is not loaded by the system class loader,
+     * which should have loaded this <code>ClassPool</code> class.
+     * The internal class loader
+     * loads only the classes explicitly specified by this method
+     * <code>writeAsClass()</code>.  The other classes are loaded
+     * by the parent class loader (the sytem class loader) by delegation.
+     * Thus, if a class <code>X</code> loaded by the internal class
+     * loader refers to a class <code>Y</code>, then the class
+     * <code>Y</code> is loaded by the parent class loader.
+     *
+     * @param classname                a fully-qualified class name.
+     *
+     * @see #forName(String)
+     * @see javassist.CtClass#toClass()
+     * @see javassist.Loader
+     */
+    public Class writeAsClass(String classname)
+       throws NotFoundException, IOException, CannotCompileException
+    {
+       try {
+           return classLoader.loadClass(classname, write(classname));
+       }
+       catch (ClassFormatError e) {
+           throw new CannotCompileException(e, classname);
+       }
+    }
+
+    /**
+     * Returns a byte array representing the class file.
+     * It calls <code>onWrite()</code> on a translator.
+     *
+     * @param classname                a fully-qualified class name.
+     */
+    public byte[] write(String classname)
+       throws NotFoundException, IOException, CannotCompileException
+    {
+       ByteArrayOutputStream barray = new ByteArrayOutputStream();
+       DataOutputStream out = new DataOutputStream(barray);
+       write(classname, out, true);
+       out.close();
+       return barray.toByteArray();
+    }
+
+    /**
+     * Writes a class file specified by <code>classname</code>
+     * to a given output stream.
+     * It calls <code>onWrite()</code> on a translator.
+     *
+     * <p>This method does not close the output stream in the end.
+     *
+     * @param classname                a fully-qualified class name.
+     * @param out              an output stream
+     */
+    public void write(String classname, DataOutputStream out)
+       throws NotFoundException, CannotCompileException, IOException
+    {
+       write(classname, out, true);
+    }
+
+    private void write(String classname, DataOutputStream out,
+                      boolean callback)
+       throws NotFoundException, CannotCompileException, IOException
+    {
+       CtClass clazz = (CtClass)classes.get(classname);
+       if (callback && translator != null
+                               && (clazz == null || !clazz.isFrozen())) {
+           translator.onWrite(this, classname);
+           // The CtClass object might be overwritten.
+           clazz = (CtClass)classes.get(classname);
+       }
+
+       if (clazz == null || !clazz.isModified())
+           source.write(classname, out);
+       else
+           clazz.toBytecode(out);
+    }
+
+    /* for CtClassType.getClassFile2()
+     */
+    byte[] readSource(String classname)
+       throws NotFoundException, IOException, CannotCompileException
+    {
+       return source.write(classname);
+    }
+
+    /*
+     * Is invoked by CtClassType.setName().
+     */
+    synchronized void classNameChanged(String oldname, CtClass clazz) {
+       CtClass c = (CtClass)classes.get(oldname);
+       if (c == clazz)         // must check this equation
+           classes.remove(c);
+
+       String newName = clazz.getName();
+       checkNotFrozen(newName, "the class with the new name is frozen.");
+       classes.put(newName, clazz);
+    }
+
+    /*
+     * Is invoked by CtClassType.setName() and methods in this class.
+     */
+    void checkNotFrozen(String classname, String errmsg)
+       throws RuntimeException
+    {
+       CtClass c = (CtClass)classes.get(classname);
+       if (c != null && c.isFrozen())
+           throw new RuntimeException(errmsg);
+    }
+
+    /**
+     * Reads a class file and constructs a <code>CtClass</code>
+     * object with a new name.
+     * This method is useful if that class file has been already
+     * loaded and the resulting class is frozen.
+     *
+     * @param orgName  the original (fully-qualified) class name
+     * @param newName  the new class name
+     */
+    public CtClass getAndRename(String orgName, String newName)
+       throws NotFoundException
+    {
+       CtClass clazz = get0(orgName);
+       clazz.setName(newName);         // indirectly calls
+                                       // classNameChanged() in this class
+       return clazz;
+    }
+
+    /**
+     * Reads a class file from the source and returns a reference
+     * to the <code>CtClass</code>
+     * object representing that class file.  If that class file has been
+     * already read, this method returns a reference to the
+     * <code>CtClass</code> created when that class file was read at the
+     * first time.
+     *
+     * <p>If <code>classname</code> ends with "[]", then this method
+     * returns a <code>CtClass</code> object for that array type.
+     *
+     * @param classname                a fully-qualified class name.
+     */
+    public synchronized CtClass get(String classname)
+       throws NotFoundException
+    {
+       CtClass clazz = (CtClass)classes.get(classname);
+       if (clazz == null) {
+           clazz = get0(classname);
+           classes.put(classname, clazz);
+       }
+
+       return clazz;
+    }
+
+    private CtClass get0(String classname) throws NotFoundException {
+       if (classname.endsWith("[]"))
+           return new CtArray(classname, this);
+       else {
+           checkClassName(classname);
+           return new CtClassType(classname, this);
+       }
+    }
+
+    /**
+     * Reads class files from the source and returns an array of
+     * <code>CtClass</code>
+     * objects representing those class files.
+     *
+     * <p>If an element of <code>classnames</code> ends with "[]",
+     * then this method
+     * returns a <code>CtClass</code> object for that array type.
+     *
+     * @param classnames       an array of fully-qualified class name.
+     */
+    public CtClass[] get(String[] classnames) throws NotFoundException {
+       if (classnames == null)
+           return new CtClass[0];
+
+       int num = classnames.length;
+       CtClass[] result = new CtClass[num];
+       for (int i = 0; i < num; ++i)
+           result[i] = get(classnames[i]);
+
+       return result;
+    }
+
+    /**
+     * Reads a class file and obtains a compile-time method.
+     *
+     * @param classname                the class name
+     * @param methodname       the method name
+     *
+     * @see CtClass#getDeclaredMethod(String)
+     */
+    public CtMethod getMethod(String classname, String methodname)
+       throws NotFoundException
+    {
+       CtClass c = get(classname);
+       return c.getDeclaredMethod(methodname);
+    }
+
+    /**
+     * Creates a new public class.
+     * If there already exists a class with the same name, the new class
+     * overwrites that previous class.
+     *
+     * @param classname                a fully-qualified class name.
+     * @exception RuntimeException     if the existing class is frozen.
+     */
+    public CtClass makeClass(String classname) throws RuntimeException {
+       return makeClass(classname, null);
+    }
+
+    /**
+     * Creates a new public class.
+     * If there already exists a class/interface with the same name,
+     * the new class overwrites that previous class.
+     *
+     * @param classname                a fully-qualified class name.
+     * @param superclass       the super class.
+     * @exception RuntimeException     if the existing class is frozen.
+     */
+    public synchronized CtClass makeClass(String classname, CtClass superclass)
+       throws RuntimeException
+    {
+       checkNotFrozen(classname,
+                      "the class with the given name is frozen.");
+       CtClass clazz = new CtNewClass(classname, this, false, superclass);
+       classes.put(classname, clazz);
+       return clazz;
+    }
+
+    /**
+     * Creates a new public interface.
+     * If there already exists a class/interface with the same name,
+     * the new interface overwrites that previous one.
+     *
+     * @param name             a fully-qualified interface name.
+     * @exception RuntimeException     if the existing interface is frozen.
+     */
+    public CtClass makeInterface(String name) throws RuntimeException {
+       return makeInterface(name, null);
+    }
+
+    /**
+     * Creates a new public interface.
+     * If there already exists a class/interface with the same name,
+     * the new interface overwrites that previous one.
+     *
+     * @param name             a fully-qualified interface name.
+     * @param superclass       the super interface.
+     * @exception RuntimeException     if the existing interface is frozen.
+     */
+    public synchronized CtClass makeInterface(String name, CtClass superclass)
+       throws RuntimeException
+    {
+       checkNotFrozen(name,
+                      "the interface with the given name is frozen.");
+       CtClass clazz = new CtNewClass(name, this, true, superclass);
+       classes.put(name, clazz);
+       return clazz;
+    }
+
+    /**
+     * Throws an exception if the class with the specified name does not
+     * exist.
+     */
+    void checkClassName(String classname)
+       throws NotFoundException
+    {
+       source.checkClassName(classname);
+    }
+
+    /**
+     * Appends the system search path to the end of the
+     * search path.  The system search path
+     * usually includes the platform library, extension
+     * libraries, and the search path specified by the
+     * <code>-classpath</code> option or the <code>CLASSPATH</code>
+     * environment variable.
+     */
+    public void appendSystemPath() {
+       source.appendSystemPath();
+    }
+
+    /**
+     * Insert a <code>ClassPath</code> object at the head of the
+     * search path.
+     *
+     * @see javassist.ClassPath
+     * @see javassist.URLClassPath
+     * @see javassist.ByteArrayClassPath
+     */
+    public void insertClassPath(ClassPath cp) {
+       source.insertClassPath(cp);
+    }
+
+    /**
+     * Appends a <code>ClassPath</code> object to the end of the
+     * search path.
+     *
+     * @see javassist.ClassPath
+     * @see javassist.URLClassPath
+     * @see javassist.ByteArrayClassPath
+     */
+    public void appendClassPath(ClassPath cp) {
+       source.appendClassPath(cp);
+    }
+
+    /**
+     * Inserts a directory or a jar (or zip) file at the head of the
+     * search path.
+     *
+     * @param pathname the path name of the directory or jar file.
+     *                 It must not end with a path separator ("/").
+     * @exception NotFoundException    if the jar file is not found.
+     */
+    public void insertClassPath(String pathname)
+       throws NotFoundException
+    {
+       source.insertClassPath(pathname);
+    }
+
+    /**
+     * Appends a directory or a jar (or zip) file to the end of the
+     * search path.
+     *
+     * @param pathname the path name of the directory or jar file.
+     *                 It must not end with a path separator ("/").
+     * @exception NotFoundException    if the jar file is not found.
+     */
+    public void appendClassPath(String pathname)
+       throws NotFoundException
+    {
+       source.appendClassPath(pathname);
+    }
+
+    /**
+     * Appends directories and jar files for search.
+     *
+     * <p>The elements of the given path list must be separated by colons
+     * in Unix or semi-colons in Windows.
+     *
+     * @param pathlist         a (semi)colon-separated list of
+     *                         the path names of directories and jar files.
+     *                         The directory name must not end with a path
+     *                         separator ("/").
+     *
+     * @exception NotFoundException    if a jar file is not found.
+     */
+    public void appendPathList(String pathlist) throws NotFoundException {
+       char sep = File.pathSeparatorChar;
+       int i = 0;
+       for (;;) {
+           int j = pathlist.indexOf(sep, i);
+           if (j < 0) {
+               appendClassPath(pathlist.substring(i));
+               break;
+           }
+           else {
+               appendClassPath(pathlist.substring(i, j));
+               i = j + 1;
+           }
+       }
+    }
+}
+
diff --git a/src/main/javassist/ClassPoolTail.java b/src/main/javassist/ClassPoolTail.java
new file mode 100644 (file)
index 0000000..1c6c705
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.*;
+import java.util.zip.*;
+
+final class ClassPathList {
+    ClassPathList next;
+    ClassPath path;
+
+    ClassPathList(ClassPath p, ClassPathList n) {
+       next = n;
+       path = p;
+    }
+}
+
+
+final class SystemClassPath implements ClassPath {
+    Class thisClass;
+
+    SystemClassPath() {
+       /* The value of thisClass was this.getClass() in early versions:
+        *
+        *     thisClass = this.getClass();
+        *
+        * However, this made openClassfile() not search all the system
+        * class paths if javassist.jar is put in jre/lib/ext/
+        * (with JDK1.4).
+        */
+       thisClass = java.lang.Object.class;
+    }
+
+    public InputStream openClassfile(String classname) {
+       String jarname = "/" + classname.replace('.', '/') + ".class";
+       return thisClass.getResourceAsStream(jarname);
+    }
+
+    public String toString() {
+       return "*system class path*";
+    }
+}
+
+
+final class DirClassPath implements ClassPath {
+    String directory;
+
+    DirClassPath(String dirName) {
+       directory = dirName;
+    }
+
+    public InputStream openClassfile(String classname) {
+       try {
+           char sep = File.separatorChar;
+           String filename = directory + sep
+               + classname.replace('.', sep) + ".class";
+           return new FileInputStream(filename.toString());
+       }
+       catch (FileNotFoundException e) {}
+       catch (SecurityException e) {}
+       return null;
+    }
+
+    public String toString() {
+       return directory;
+    }
+}
+
+
+final class JarClassPath implements ClassPath {
+    ZipFile jarfile;
+
+    JarClassPath(String pathname) throws NotFoundException {
+       try {
+           jarfile = new ZipFile(pathname);
+           return;
+       }
+       catch (ZipException e) {}
+       catch (IOException e) {}
+       throw new NotFoundException(pathname);
+    }
+
+    public InputStream openClassfile(String classname)
+       throws NotFoundException
+    {
+       try {
+           String jarname = classname.replace('.', '/') + ".class";
+           ZipEntry ze = jarfile.getEntry(jarname);
+           if (ze != null)
+               return jarfile.getInputStream(ze);
+           else
+               return null;    // not found
+       }
+       catch (ZipException e) {}
+       catch (IOException e) {}
+       throw new NotFoundException("broken jar file?: "
+                                   + jarfile.getName());
+    }
+
+    public String toString() {
+       return jarfile.toString();
+    }
+}
+
+final class ClassPoolTail extends ClassPool {
+    protected ClassPathList pathList;
+    private Class thisClass;
+
+    public ClassPoolTail() {
+       pathList = null;
+       thisClass = getClass();
+    }
+
+    public String toString() {
+       StringBuffer buf = new StringBuffer();
+       buf.append("[class path: ");
+       ClassPathList list = pathList;
+       while (list != null) {
+           buf.append(list.path.toString());
+           buf.append(File.pathSeparatorChar);
+           list = list.next;
+       }
+
+       buf.append(']');
+       return buf.toString();
+    }
+
+    public byte[] write(String classname)
+       throws NotFoundException, IOException
+    {
+       return readClassfile(classname);
+    }
+
+    public void write(String classname, DataOutputStream out)
+       throws NotFoundException, CannotCompileException, IOException
+    {
+       byte[] b = write(classname);
+       out.write(b, 0, b.length);
+    }
+
+    public CtClass get(String classname) throws NotFoundException {
+       throw new RuntimeException("fatal error");
+    }
+
+    public CtClass makeClass(String classname) {
+       throw new RuntimeException("fatal error");
+    }
+
+    void checkClassName(String classname)
+       throws NotFoundException
+    {
+       InputStream fin = openClassfile(classname);
+       try {
+           fin.close();
+       }
+       catch (IOException e) { /* ignore */ }
+    }
+
+    public synchronized void insertClassPath(ClassPath cp) {
+       pathList = new ClassPathList(cp, pathList);
+    }
+
+    public synchronized void appendClassPath(ClassPath cp) {
+       ClassPathList tail = new ClassPathList(cp, null);
+       ClassPathList list = pathList;
+       if (list == null)
+           pathList = tail;
+       else {
+           while (list.next != null)
+               list = list.next;
+
+           list.next = tail;
+       }
+    }
+
+    public void appendSystemPath() {
+       appendClassPath(new SystemClassPath());
+    }
+
+    public void insertClassPath(String pathname)
+       throws NotFoundException
+    {
+       insertClassPath(makePathObject(pathname));
+    }
+
+    public void appendClassPath(String pathname)
+       throws NotFoundException
+    {
+       appendClassPath(makePathObject(pathname));
+    }
+
+    private static ClassPath makePathObject(String pathname)
+       throws NotFoundException
+    {
+       if (pathname.endsWith(".jar") || pathname.endsWith(".zip")
+           || pathname.endsWith(".JAR") || pathname.endsWith(".ZIP"))
+           return new JarClassPath(pathname);
+       else
+           return new DirClassPath(pathname);
+    }
+
+    /**
+     * Obtains the contents of the class file for the class
+     * specified by <code>classname</code>.
+     *
+     * @param classname                a fully-qualified class name
+     */
+    byte[] readClassfile(String classname)
+       throws NotFoundException, IOException
+    {
+       InputStream fin = openClassfile(classname);
+       byte[] b = readStream(fin);
+       fin.close();
+       return b;
+    }
+
+    /**
+     * Opens the class file for the class specified by
+     * <code>classname</code>.
+     *
+     * @param classname                a fully-qualified class name
+     */
+    public InputStream openClassfile(String classname)
+       throws NotFoundException
+    {
+       ClassPathList list = pathList;
+       InputStream ins = null;
+       NotFoundException error = null;
+       while (list != null) {
+           try {
+               ins = list.path.openClassfile(classname);
+           }
+           catch (NotFoundException e) {
+               if (error == null)
+                   error = e;
+           }
+
+           if (ins == null)
+               list = list.next;
+           else
+               return ins;
+       }
+
+       if (error != null)
+           throw error;
+       else
+           throw new NotFoundException(classname);
+    }
+
+    /**
+     * Reads an input stream until it reaches the end.
+     *
+     * @return         the contents of that input stream
+     */
+    public static byte[] readStream(InputStream fin) throws IOException {
+       byte[][] bufs = new byte[8][];
+       int bufsize = 4096;
+
+       for (int i = 0; i < 8; ++i) {
+           bufs[i] = new byte[bufsize];
+           int size = 0;
+           int len = 0;
+           do {
+               len = fin.read(bufs[i], size, bufsize - size);
+               if (len >= 0)
+                   size += len;
+               else {
+                   byte[] result = new byte[bufsize - 4096 + size];
+                   int s = 0;
+                   for (int j = 0; j < i; ++j) {
+                       System.arraycopy(bufs[j], 0, result, s, s + 4096);
+                       s = s + s + 4096;
+                   }
+
+                   System.arraycopy(bufs[i], 0, result, s, size);
+                   return result;
+               }
+           } while (size < bufsize);
+           bufsize *= 2;
+       }
+
+       throw new IOException("too much data");
+    }
+}
diff --git a/src/main/javassist/CodeConverter.java b/src/main/javassist/CodeConverter.java
new file mode 100644 (file)
index 0000000..051699b
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.convert.*;
+
+/**
+ * Simple translator of method bodies
+ * (also see the <code>javassist.expr</code> package).
+ *
+ * <p>Instances of this class specifies how to instrument of the
+ * bytecodes representing a method body.  They are passed to
+ * <code>CtClass.instrument()</code> or
+ * <code>CtMethod.instrument()</code> as a parameter.
+ *
+ * <p>Example:
+ * <ul><pre>
+ * ClassPool cp = ClassPool.getDefault();
+ * CtClass point = cp.get("Point");
+ * CtClass singleton = cp.get("Singleton");
+ * CtClass client = cp.get("Client");
+ * CodeConverter conv = new CodeConverter();
+ * conv.replaceNew(point, singleton, "makePoint");
+ * client.instrument(conv);
+ * </pre></ul>
+ *
+ * <p>This program substitutes "<code>Singleton.makePoint()</code>"
+ * for all occurrences of "<code>new Point()</code>"
+ * appearing in methods declared in a <code>Client</code> class.
+ *
+ * @see javassist.CtClass#instrument(CodeConverter)
+ * @see javassist.CtMethod#instrument(CodeConverter)
+ * @see javassist.expr.ExprEditor
+ */
+public class CodeConverter {
+    Transformer transformers = null;
+
+    /**
+     * Modify a method body so that instantiation of the specified class
+     * is replaced with a call to the specified static method.  For example,
+     * <code>replaceNew(ctPoint, ctSingleton, "createPoint")</code>
+     * (where <code>ctPoint</code> and <code>ctSingleton</code> are
+     * compile-time classes for class <code>Point</code> and class
+     * <code>Singleton</code>, respectively)
+     * replaces all occurrences of:
+     *
+     * <ul><code>new Point(x, y)</code></ul>
+     *
+     * in the method body with:
+     *
+     * <ul><code>Singleton.createPoint(x, y)</code></ul>
+     * 
+     * <p>This enables to intercept instantiation of <code>Point</code>
+     * and change the samentics.  For example, the following
+     * <code>createPoint()</code> implements the singleton pattern:
+     *
+     * <ul><pre>public static Point createPoint(int x, int y) {
+     *     if (aPoint == null)
+     *         aPoint = new Point(x, y);
+     *     return aPoint;
+     * }
+     * </pre></ul>
+     *
+     * <p>The static method call substituted for the original <code>new</code>
+     * expression must be
+     * able to receive the same set of parameters as the original
+     * constructor.  If there are multiple constructors with different
+     * parameter types, then there must be multiple static methods
+     * with the same name but different parameter types.
+     *
+     * <p>The return type of the substituted static method must be
+     * the exactly same as the type of the instantiated class specified by
+     * <code>newClass</code>.
+     *
+     * @param newClass         the instantiated class.
+     * @param calledClass      the class in which the static method is
+     *                         declared.
+     * @param calledMethod     the name of the static method.
+     */
+    public void replaceNew(CtClass newClass,
+                          CtClass calledClass, String calledMethod) {
+       transformers = new TransformNew(transformers, newClass.getName(),
+                                       calledClass.getName(), calledMethod);
+    }
+
+    /**
+     * Modify a method body so that field read/write expressions access
+     * a different field from the original one.
+     *
+     * <p>Note that this method changes only the filed name and the class
+     * declaring the field; the type of the target object does not change.
+     * Therefore, the substituted field must be declared in the same class
+     * or a superclass of the original class.
+     *
+     * <p>Also, <code>clazz</code> and <code>newClass</code> must specify
+     * the class directly declaring the field.  They must not specify
+     * a subclass of that class.
+     *
+     * @param field            the originally accessed field.
+     * @param newClass the class declaring the substituted field.
+     * @param newFieldname     the name of the substituted field.
+     */
+    public void redirectFieldAccess(CtField field,
+                                   CtClass newClass, String newFieldname) {
+       transformers = new TransformFieldAccess(transformers, field,
+                                               newClass.getName(),
+                                               newFieldname);
+    }
+
+    /**
+     * Modify a method body so that an expression reading the specified
+     * field is replaced with a call to the specified <i>static</i> method.
+     * This static method receives the target object of the original
+     * read expression as a parameter.  It must return a value of
+     * the same type as the field.
+     *
+     * <p>For example, the program below
+     *
+     * <ul><pre>Point p = new Point();
+     * int newX = p.x + 3;</pre></ul>
+     *
+     * <p>can be translated into:
+     *
+     * <ul><pre>Point p = new Point();
+     * int newX = Accessor.readX(p) + 3;</pre></ul>
+     *
+     * <p>where
+     *
+     * <ul><pre>public class Accessor {
+     *     public static int readX(Object target) { ... }
+     * }</pre></ul>
+     *
+     * <p>The type of the parameter of <code>readX()</code> must
+     * be <code>java.lang.Object</code> independently of the actual
+     * type of <code>target</code>.  The return type must be the same
+     * as the field type.
+     *
+     * @param field            the field.
+     * @param calledClass      the class in which the static method is
+     *                         declared.
+     * @param calledMethod     the name of the static method.
+     */
+    public void replaceFieldRead(CtField field,
+                                CtClass calledClass, String calledMethod) {
+       transformers = new TransformReadField(transformers, field,
+                                             calledClass.getName(),
+                                             calledMethod);
+    }
+
+    /**
+     * Modify a method body so that an expression writing the specified
+     * field is replaced with a call to the specified static method.
+     * This static method receives two parameters: the target object of
+     * the original
+     * write expression and the assigned value.  The return type of the
+     * static method is <code>void</code>.
+     * 
+     * <p>For example, the program below
+     *
+     * <ul><pre>Point p = new Point();
+     * p.x = 3;</pre></ul>
+     *
+     * <p>can be translated into:
+     *
+     * <ul><pre>Point p = new Point();
+     * Accessor.writeX(3);</pre></ul>
+     *
+     * <p>where
+     *
+     * <ul><pre>public class Accessor {
+     *     public static void writeX(Object target, int value) { ... }
+     * }</pre></ul>
+     *
+     * <p>The type of the first parameter of <code>writeX()</code> must
+     * be <code>java.lang.Object</code> independently of the actual
+     * type of <code>target</code>.  The type of the second parameter
+     * is the same as the field type.
+     *
+     * @param field            the field.
+     * @param calledClass      the class in which the static method is
+     *                         declared.
+     * @param calledMethod     the name of the static method.
+     */
+    public void replaceFieldWrite(CtField field,
+                                 CtClass calledClass, String calledMethod) {
+       transformers = new TransformWriteField(transformers, field,
+                                              calledClass.getName(),
+                                              calledMethod);
+    }
+
+    /**
+     * Modify method invocations in a method body so that a different
+     * method is invoked.
+     *
+     * <p>Note that the target object, the parameters, or 
+     * the type of invocation
+     * (static method call, interface call, or private method call)
+     * are not modified.  Only the method name is changed.  The substituted
+     * method must have the same signature that the original one has.
+     * If the original method is a static method, the substituted method
+     * must be static.
+     *
+     * @param origMethod       original method
+     * @param substMethod      substituted method
+     */
+    public void redirectMethodCall(CtMethod origMethod,
+                                  CtMethod substMethod)
+       throws CannotCompileException
+    {
+       String d1 = origMethod.getMethodInfo2().getDescriptor();
+       String d2 = substMethod.getMethodInfo2().getDescriptor();
+       if (!d1.equals(d2))
+           throw new CannotCompileException("signature mismatch");
+
+       transformers = new TransformCall(transformers, origMethod,
+                                        substMethod);
+    }
+
+    /**
+     * Insert a call to another method before an existing method call.
+     * That "before" method must be static.  The return type must be
+     * <code>void</code>.  As parameters, the before method receives
+     * the target object and all the parameters to the originally invoked
+     * method.  For example, if the originally invoked method is
+     * <code>move()</code>:
+     *
+     * <ul><pre>class Point {
+     *     Point move(int x, int y) { ... }
+     * }</pre></ul>
+     *
+     * <p>Then the before method must be something like this:
+     *
+     * <ul><pre>class Verbose {
+     *     static void print(Point target, int x, int y) { ... }
+     * }</pre></ul>
+     *
+     * <p>The <code>CodeConverter</code> would translate bytecode
+     * equivalent to:
+     *
+     * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
+     *
+     * <p>into the bytecode equivalent to:
+     *
+     * <ul><pre>int tmp1 = x + y;
+     * int tmp2 = 0;
+     * Verbose.print(p, tmp1, tmp2);
+     * Point p2 = p.move(tmp1, tmp2);</pre></ul>
+     *
+     * @param origMethod       the method originally invoked.
+     * @param beforeMethod     the method invoked before
+     *                         <code>origMethod</code>.
+     */
+    public void insertBeforeMethod(CtMethod origMethod,
+                                  CtMethod beforeMethod)
+       throws CannotCompileException
+    {
+       try {
+           transformers = new TransformBefore(transformers, origMethod,
+                                              beforeMethod);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    /**
+     * Inserts a call to another method after an existing method call.
+     * That "after" method must be static.  The return type must be
+     * <code>void</code>.  As parameters, the after method receives
+     * the target object and all the parameters to the originally invoked
+     * method.  For example, if the originally invoked method is
+     * <code>move()</code>:
+     *
+     * <ul><pre>class Point {
+     *     Point move(int x, int y) { ... }
+     * }</pre></ul>
+     *
+     * <p>Then the after method must be something like this:
+     *
+     * <ul><pre>class Verbose {
+     *     static void print(Point target, int x, int y) { ... }
+     * }</pre></ul>
+     *
+     * <p>The <code>CodeConverter</code> would translate bytecode
+     * equivalent to:
+     *
+     * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
+     *
+     * <p>into the bytecode equivalent to:
+     *
+     * <ul><pre>int tmp1 = x + y;
+     * int tmp2 = 0;
+     * Point p2 = p.move(tmp1, tmp2);
+     * Verbose.print(p, tmp1, tmp2);</pre></ul>
+     *
+     * @param origMethod       the method originally invoked.
+     * @param afterMethod      the method invoked after
+     *                         <code>origMethod</code>.
+     */
+    public void insertAfterMethod(CtMethod origMethod,
+                                 CtMethod afterMethod)
+       throws CannotCompileException
+    {
+       try {
+           transformers = new TransformAfter(transformers, origMethod,
+                                              afterMethod);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    /**
+     * Performs code conversion.
+     */
+    void doit(CtClass clazz, MethodInfo minfo, ConstPool cp)
+       throws CannotCompileException
+    {
+       Transformer t;
+
+       CodeAttribute codeAttr = minfo.getCodeAttribute();
+       if (codeAttr == null || transformers == null)
+           return;
+
+       for (t = transformers; t != null; t = t.getNext())
+           t.initialize(cp, codeAttr);
+
+       CodeIterator iterator = codeAttr.iterator();
+       while (iterator.hasNext()) {
+           try {
+               int pos = iterator.next();
+               for (t = transformers; t != null; t = t.getNext())
+                   pos = t.transform(clazz, pos, iterator, cp);
+           }
+           catch (BadBytecode e) {
+               throw new CannotCompileException(e);
+           }
+       }
+
+       int locals = 0;
+       for (t = transformers; t != null; t = t.getNext()) {
+           int s = t.extraLocals();
+           if (s > locals)
+               locals = s;
+       }
+
+       for (t = transformers; t != null; t = t.getNext())
+           t.clean();
+
+       codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals);
+    }
+}
diff --git a/src/main/javassist/CtArray.java b/src/main/javassist/CtArray.java
new file mode 100644 (file)
index 0000000..904f15c
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+/**
+ * Array types.
+ */
+final class CtArray extends CtClass {
+    protected ClassPool pool;
+
+    // the name of array type ends with "[]".
+    CtArray(String name, ClassPool cp) {
+       super(name);
+       pool = cp;
+    }
+
+    public ClassPool getClassPool() {
+       return pool;
+    }
+
+    public boolean isArray() {
+       return true;
+    }
+
+    public boolean subtypeOf(CtClass clazz) throws NotFoundException {
+       if (super.subtypeOf(clazz))
+           return true;
+
+       String cname = clazz.getName();
+       if (cname.equals(javaLangObject)
+           || cname.equals("java.lang.Cloneable"))
+           return true;
+
+       return clazz.isArray()
+           && getComponentType().subtypeOf(clazz.getComponentType());
+    }
+
+    public CtClass getComponentType() throws NotFoundException {
+       String name = getName();
+       return pool.get(name.substring(0, name.length() - 2));
+    }
+
+    public CtClass getSuperclass() throws NotFoundException {
+       return pool.get(javaLangObject);
+    }
+
+    public CtMethod[] getMethods() {
+       try {
+           return getSuperclass().getMethods();
+       }
+       catch (NotFoundException e) {
+           return super.getMethods();
+       }
+    }
+
+    public CtMethod getMethod(String name, String desc)
+       throws NotFoundException
+    {
+       return getSuperclass().getMethod(name, desc);
+    }
+
+    public CtConstructor[] getConstructors() {
+       try {
+           return getSuperclass().getConstructors();
+       }
+       catch (NotFoundException e) {
+           return super.getConstructors();
+       }
+    }
+}
diff --git a/src/main/javassist/CtBehavior.java b/src/main/javassist/CtBehavior.java
new file mode 100644 (file)
index 0000000..220612b
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.compiler.Javac;
+import javassist.compiler.CompileError;
+import javassist.expr.ExprEditor;
+
+/**
+ * <code>CtBehavior</code> is the abstract super class of
+ * <code>CtMethod</code> and <code>CtConstructor</code>.
+ */
+public abstract class CtBehavior extends CtMember {
+    protected MethodInfo methodInfo;
+
+    protected CtBehavior(CtClass clazz, MethodInfo minfo) {
+       super(clazz);
+       methodInfo = minfo;
+    }
+
+    /**
+     * Returns the MethodInfo representing this member in the
+     * class file.
+     */
+    public MethodInfo getMethodInfo() {
+       declaringClass.checkModify();
+       return methodInfo;
+    }
+
+    /**
+     * Undocumented method.  Do not use; internal-use only.
+     */
+    public MethodInfo getMethodInfo2() { return methodInfo; }
+
+    /**
+     * Obtains the modifiers of the member.
+     *
+     * @return         modifiers encoded with
+     *                 <code>javassist.Modifier</code>.
+     * @see Modifier
+     */
+    public int getModifiers() {
+       return AccessFlag.toModifier(methodInfo.getAccessFlags());
+    }
+
+    /**
+     * Sets the encoded modifiers of the member.
+     *
+     * @see Modifier
+     */
+    public void setModifiers(int mod) {
+       declaringClass.checkModify();
+       methodInfo.setAccessFlags(AccessFlag.of(mod));
+    }
+
+    /**
+     * Obtains the name of this member.
+     *
+     * @see CtConstructor#getName()
+     */
+    public abstract String getName();
+
+    /**
+     * Obtains parameter types of this member.
+     */
+    public CtClass[] getParameterTypes() throws NotFoundException {
+       return Descriptor.getParameterTypes(methodInfo.getDescriptor(),
+                                           declaringClass.getClassPool());
+    }
+
+    /**
+     * Obtains the type of the returned value.
+     */
+    CtClass getReturnType0() throws NotFoundException {
+       return Descriptor.getReturnType(methodInfo.getDescriptor(),
+                                       declaringClass.getClassPool());
+    }
+
+    /**
+     * Returns the character string representing the parameter types
+     * and the return type.  If two members have the same parameter types
+     * and the return type, <code>getSignature()</code> returns the
+     * same string.
+     */
+    public String getSignature() {
+       return methodInfo.getDescriptor();
+    }
+
+    /**
+     * Obtains exceptions that this member may throw.
+     */
+    public CtClass[] getExceptionTypes() throws NotFoundException {
+       String[] exceptions;
+       ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
+       if (ea == null)
+           exceptions = null;
+       else
+           exceptions = ea.getExceptions();
+
+       return declaringClass.getClassPool().get(exceptions);
+    }
+
+    /**
+     * Sets exceptions that this member may throw.
+     */
+    public void setExceptionTypes(CtClass[] types) throws NotFoundException {
+       declaringClass.checkModify();
+       if (types == null) {
+           methodInfo.removeExceptionsAttribute();
+           return;
+       }
+
+       String[] names = new String[types.length];
+       for (int i = 0; i < types.length; ++i)
+           names[i] = types[i].getName();
+
+       ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
+       if (ea == null) {
+           ea = new ExceptionsAttribute(methodInfo.getConstPool());
+           methodInfo.setExceptionsAttribute(ea);
+       }
+
+       ea.setExceptions(names);
+    }
+
+    /**
+     * Sets a member body.
+     *
+     * @param src      the source code representing the member body.
+     *                 It must be a single statement or block.
+     */
+    public void setBody(String src) throws CannotCompileException {
+       declaringClass.checkModify();
+       try {
+           Javac jv = new Javac(declaringClass);
+           Bytecode b = jv.compileBody(this, src);
+           methodInfo.setCodeAttribute(b.toCodeAttribute());
+           methodInfo.setAccessFlags(methodInfo.getAccessFlags()
+                                     & ~AccessFlag.ABSTRACT);
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    static void setBody0(CtClass srcClass, MethodInfo srcInfo,
+                        CtClass destClass, MethodInfo destInfo,
+                        ClassMap map)
+       throws CannotCompileException
+    {
+       destClass.checkModify();
+
+       if (map == null)
+           map = new ClassMap();
+
+       map.put(srcClass.getName(), destClass.getName());
+       try {
+           CodeAttribute cattr = srcInfo.getCodeAttribute();
+           if (cattr != null) {
+               ConstPool cp = destInfo.getConstPool();
+               CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map);
+               destInfo.setCodeAttribute(ca);
+           }
+       }
+       catch (CodeAttribute.RuntimeCopyException e) {
+           /* the exception may be thrown by copy() in CodeAttribute.
+            */
+           throw new CannotCompileException(e);
+       }
+
+       destInfo.setAccessFlags(destInfo.getAccessFlags()
+                               & ~AccessFlag.ABSTRACT);
+    }
+
+    /**
+     * Obtains an attribute with the given name.
+     * If that attribute is not found in the class file, this
+     * method returns null.
+     *
+     * @param name             attribute name
+     */
+    public byte[] getAttribute(String name) {
+       AttributeInfo ai = methodInfo.getAttribute(name);
+       if (ai == null)
+           return null;
+       else
+           return ai.get();
+    }
+
+    /**
+     * Adds an attribute. The attribute is saved in the class file.
+     *
+     * @param name     attribute name
+     * @param data     attribute value
+     */
+    public void setAttribute(String name, byte[] data) {
+       declaringClass.checkModify();
+       methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(),
+                                                 name, data));
+    }
+
+    /**
+     * Declares to use <code>$cflow</code> for this member;
+     * If <code>$cflow</code> is used, the class files modified
+     * with Javassist requires a support class
+     * <code>javassist.runtime.Cflow</code> at runtime
+     * (other Javassist classes are not required at runtime).
+     *
+     * <p>Every <code>$cflow</code> variable is given a unique name.
+     * For example, if the given name is <code>"Point.paint"</code>,
+     * then the variable is indicated by <code>$cflow(Point.paint)</code>.
+     *
+     * @param name     <code>$cflow</code> name.  It can include
+     *                 alphabets, numbers, <code>_</code>,
+     *                 <code>$</code>, and <code>.</code> (dot).
+     *
+     * @see javassist.runtime.Cflow
+     */
+    public void useCflow(String name) throws CannotCompileException {
+       CtClass cc = declaringClass;
+       cc.checkModify();
+       ClassPool pool = cc.getClassPool();
+       String fname;
+       int i = 0;
+       while (true) {
+           fname = "_cflow$" + i++;
+           try {
+               cc.getDeclaredField(fname);
+           }
+           catch(NotFoundException e) {
+               break;
+           }
+       }
+
+       pool.recordCflow(name, declaringClass.getName(), fname);
+       try {
+           CtClass type = pool.get("javassist.runtime.Cflow");
+           CtField field = new CtField(type, fname, cc);
+           field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
+           cc.addField(field, CtField.Initializer.byNew(type));
+           insertBefore(fname + ".enter();");
+           String src = fname + ".exit();";
+           insertAfter(src, true);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    /**
+     * Modifies the member body.
+     *
+     * @param converter                specifies how to modify.
+     */
+    public void instrument(CodeConverter converter)
+       throws CannotCompileException
+    {
+       declaringClass.checkModify();
+       ConstPool cp = methodInfo.getConstPool();
+       converter.doit(getDeclaringClass(), methodInfo, cp);
+    }
+
+    /**
+     * Modifies the member body.
+     *
+     * @param editor           specifies how to modify.
+     */
+    public void instrument(ExprEditor editor)
+       throws CannotCompileException
+    {
+       // if the class is not frozen,
+       // does not trun the modified flag on.
+       if (declaringClass.isFrozen())
+           declaringClass.checkModify();
+
+       if (editor.doit(declaringClass, methodInfo))
+           declaringClass.checkModify();
+    }
+
+    /**
+     * Inserts bytecode at the beginning of the body.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     */
+    public void insertBefore(String src) throws CannotCompileException {
+       declaringClass.checkModify();
+       CodeAttribute ca = methodInfo.getCodeAttribute();
+       CodeIterator iterator = ca.iterator();
+       Javac jv = new Javac(declaringClass);
+       try {
+           jv.recordParams(getParameterTypes(),
+                           Modifier.isStatic(getModifiers()));
+           jv.compileStmnt(src);
+           Bytecode b = jv.getBytecode();
+           int stack = b.getMaxStack();
+           int locals = b.getMaxLocals();
+
+           if (stack > ca.getMaxStack())
+               ca.setMaxStack(stack);
+
+           if (locals > ca.getMaxLocals())
+               ca.setMaxLocals(locals);
+
+           int pos = iterator.insertEx(b.get());
+           iterator.insert(b.getExceptionTable(), pos);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+       catch (BadBytecode e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    /**
+     * Inserts bytecode at the end of the body.
+     * The bytecode is inserted just before every return insturction.
+     * It is not executed when an exception is thrown.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     */
+    public void insertAfter(String src)
+       throws CannotCompileException
+    {
+       insertAfter(src, false);
+    }
+
+    /**
+     * Inserts bytecode at the end of the body.
+     * The bytecode is inserted just before every return insturction.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     * @param asFinally                true if the inserted bytecode is executed
+     *                 not only when the control normally returns
+     *                 but also when an exception is thrown.
+     */
+    public void insertAfter(String src, boolean asFinally)
+       throws CannotCompileException
+    {
+       declaringClass.checkModify();
+       CodeAttribute ca = methodInfo.getCodeAttribute();
+       CodeIterator iterator = ca.iterator();
+       int retAddr = ca.getMaxLocals();
+       Bytecode b = new Bytecode(methodInfo.getConstPool(), 0, retAddr + 1);
+       b.setStackDepth(ca.getMaxStack() + 1);
+       Javac jv = new Javac(b, declaringClass);
+       try {
+           jv.recordParams(getParameterTypes(),
+                           Modifier.isStatic(getModifiers()));
+           CtClass rtype = getReturnType0();
+           int varNo = jv.recordReturnType(rtype, true);
+           boolean isVoid = rtype == CtClass.voidType;
+
+           int handlerLen = insertAfterHandler(asFinally, b, rtype);
+
+           b.addAstore(retAddr);
+           if (isVoid) {
+               b.addOpcode(Opcode.ACONST_NULL);
+               b.addAstore(varNo);
+               jv.compileStmnt(src);
+           }
+           else {
+               b.addStore(varNo, rtype);
+               jv.compileStmnt(src);
+               b.addLoad(varNo, rtype);
+           }
+
+           b.addRet(retAddr);
+           ca.setMaxStack(b.getMaxStack());
+           ca.setMaxLocals(b.getMaxLocals());
+
+           int gapPos = iterator.append(b.get());
+           iterator.append(b.getExceptionTable(), gapPos);
+
+           if (asFinally)
+               ca.getExceptionTable().add(0, gapPos, gapPos, 0);
+
+           int gapLen = iterator.getCodeLength() - gapPos - handlerLen;
+           int subr = iterator.getCodeLength() - gapLen;
+
+           while (iterator.hasNext()) {
+               int pos = iterator.next();
+               if (pos >= subr)
+                   break;
+
+               int c = iterator.byteAt(pos);
+               if (c == Opcode.ARETURN || c == Opcode.IRETURN
+                   || c == Opcode.FRETURN || c == Opcode.LRETURN
+                   || c == Opcode.DRETURN || c == Opcode.RETURN) {
+                   if (subr - pos > Short.MAX_VALUE - 5) {
+                       iterator.insertGap(pos, 5);
+                       iterator.writeByte(Opcode.JSR_W, pos);
+                       iterator.write32bit(subr - pos + 5, pos + 1);
+                   }
+                   else {
+                       iterator.insertGap(pos, 3);
+                       iterator.writeByte(Opcode.JSR, pos);
+                       iterator.write16bit(subr - pos + 3, pos + 1);
+                   }
+
+                   subr = iterator.getCodeLength() - gapLen;
+               }
+           }
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+       catch (BadBytecode e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    private int insertAfterHandler(boolean asFinally, Bytecode b,
+                                  CtClass rtype)
+    {
+       if (!asFinally)
+           return 0;
+
+       int var = b.getMaxLocals();
+       b.incMaxLocals(1);
+       int pc = b.currentPc();
+       b.addAstore(var);
+       if (rtype.isPrimitive()) {
+           char c = ((CtPrimitiveType)rtype).getDescriptor();
+           if (c == 'D')
+               b.addDconst(0.0);
+           else if (c == 'F')
+               b.addFconst(0);
+           else if (c == 'J')
+               b.addLconst(0);
+           else if (c != 'V')  // int, boolean, char, short, ...
+               b.addIconst(0);
+       }
+       else
+           b.addOpcode(Opcode.ACONST_NULL);
+
+       b.addOpcode(Opcode.JSR);
+       int pc2 = b.currentPc();
+       b.addIndex(0);  // correct later
+       b.addAload(var);
+       b.addOpcode(Opcode.ATHROW);
+       int pc3 = b.currentPc();
+       b.write16bit(pc2, pc3 - pc2 + 1);
+       return pc3 - pc;
+    }
+
+    /* -- OLD version --
+
+    public void insertAfter(String src) throws CannotCompileException {
+       declaringClass.checkModify();
+       CodeAttribute ca = methodInfo.getCodeAttribute();
+       CodeIterator iterator = ca.iterator();
+       Bytecode b = new Bytecode(methodInfo.getConstPool(),
+                                 ca.getMaxStack(), ca.getMaxLocals());
+       b.setStackDepth(ca.getMaxStack());
+       Javac jv = new Javac(b, declaringClass);
+       try {
+           jv.recordParams(getParameterTypes(),
+                           Modifier.isStatic(getModifiers()));
+           CtClass rtype = getReturnType0();
+           int varNo = jv.recordReturnType(rtype, true);
+           boolean isVoid = rtype == CtClass.voidType;
+           if (isVoid) {
+               b.addOpcode(Opcode.ACONST_NULL);
+               b.addAstore(varNo);
+               jv.compileStmnt(src);
+           }
+           else {
+               b.addStore(varNo, rtype);
+               jv.compileStmnt(src);
+               b.addLoad(varNo, rtype);
+           }
+
+           byte[] code = b.get();
+           ca.setMaxStack(b.getMaxStack());
+           ca.setMaxLocals(b.getMaxLocals());
+           while (iterator.hasNext()) {
+               int pos = iterator.next();
+               int c = iterator.byteAt(pos);
+               if (c == Opcode.ARETURN || c == Opcode.IRETURN
+                   || c == Opcode.FRETURN || c == Opcode.LRETURN
+                   || c == Opcode.DRETURN || c == Opcode.RETURN)
+                   iterator.insert(pos, code);
+           }
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+       catch (BadBytecode e) {
+           throw new CannotCompileException(e);
+       }
+    }
+    */
+
+    /**
+     * Adds a catch clause that handles an exception thrown in the
+     * body.  The catch clause must end with a return or throw statement.
+     *
+     * @param src      the source code representing the catch clause.
+     *                 It must be a single statement or block.
+     * @param exceptionType    the type of the exception handled by the
+     *                         catch clause.
+     * @param exceptionName    the name of the variable containing the
+     *                         caught exception.
+     */
+    public void addCatch(String src, CtClass exceptionType,
+                        String exceptionName)
+       throws CannotCompileException
+    {
+       declaringClass.checkModify();
+       ConstPool cp = methodInfo.getConstPool();
+       CodeAttribute ca = methodInfo.getCodeAttribute();
+       CodeIterator iterator = ca.iterator();
+       Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals());
+       b.setStackDepth(1);
+       Javac jv = new Javac(b, declaringClass);
+       try {
+           jv.recordParams(getParameterTypes(),
+                           Modifier.isStatic(getModifiers()));
+           int var = jv.recordVariable(exceptionType, exceptionName);
+           b.addAstore(var);
+           jv.compileStmnt(src);
+
+           int stack = b.getMaxStack();
+           int locals = b.getMaxLocals();
+
+           if (stack > ca.getMaxStack())
+               ca.setMaxStack(stack);
+
+           if (locals > ca.getMaxLocals())
+               ca.setMaxLocals(locals);
+
+           int len = iterator.getCodeLength();
+           int pos = iterator.append(b.get());
+           ca.getExceptionTable().add(0, len, len,
+                                      cp.addClassInfo(exceptionType));
+           iterator.append(b.getExceptionTable(), pos);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+    }
+}
diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java
new file mode 100644 (file)
index 0000000..1d70b8b
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import javassist.bytecode.*;
+import java.util.Collection;
+import javassist.expr.ExprEditor;
+
+// Subclasses of CtClass: CtClassType, CtPrimitiveType, and CtArray
+
+/**
+ * An instance of <code>CtClass</code> represents a class.
+ * It is obtained from <code>ClassPool</code>.
+ *
+ * @see ClassPool#get(String)
+ */
+public abstract class CtClass {
+    protected String qualifiedName;
+
+    /**
+     * The version number of this release.
+     */
+    public static final String version = "2.4";
+
+    static final String javaLangObject = "java.lang.Object";
+
+    /**
+     * The <code>CtClass</code> object representing
+     * the <code>boolean</code> type.
+     */
+    public static CtClass booleanType;
+
+    /**
+     * The <code>CtClass</code> object representing
+     * the <code>char</code> type.
+     */
+    public static CtClass charType;
+
+    /**
+     * The <code>CtClass</code> object representing
+     * the <code>byte</code> type.
+     */
+    public static CtClass byteType;
+
+    /**
+     * The <code>CtClass</code> object representing
+     * the <code>short</code> type.
+     */
+    public static CtClass shortType;
+
+    /**
+     * The <code>CtClass</code> object representing
+     * the <code>int</code> type.
+     */
+    public static CtClass intType;
+
+    /**
+     * The <code>CtClass</code> object representing
+     * the <code>long</code> type.
+     */
+    public static CtClass longType;
+
+    /**
+     * The <code>CtClass</code> object representing
+     * the <code>float</code> type.
+     */
+    public static CtClass floatType;
+
+    /**
+     * The <code>CtClass</code> object representing
+     * the <code>double</code> type.
+     */
+    public static CtClass doubleType;
+
+    /**
+     * The <code>CtClass</code> object representing
+     * the <code>void</code> type.
+     */
+    public static CtClass voidType;
+
+    static CtClass[] primitiveTypes;
+
+    static {
+       primitiveTypes = new CtClass[9];
+
+       booleanType = new CtPrimitiveType("boolean", 'Z', "java.lang.Boolean",
+                               "booleanValue", "()Z", Opcode.IRETURN,
+                               Opcode.T_BOOLEAN, 1);
+       primitiveTypes[0] = booleanType;
+
+       charType = new CtPrimitiveType("char", 'C', "java.lang.Character",
+                               "charValue", "()C", Opcode.IRETURN,
+                               Opcode.T_CHAR, 1);
+       primitiveTypes[1] = charType;
+
+       byteType = new CtPrimitiveType("byte", 'B', "java.lang.Byte",
+                               "byteValue", "()B", Opcode.IRETURN,
+                               Opcode.T_BYTE, 1);
+       primitiveTypes[2] = byteType;
+
+       shortType = new CtPrimitiveType("short", 'S', "java.lang.Short",
+                               "shortValue", "()S", Opcode.IRETURN,
+                               Opcode.T_SHORT, 1);
+       primitiveTypes[3] = shortType;
+
+       intType = new CtPrimitiveType("int", 'I', "java.lang.Integer",
+                               "intValue", "()I", Opcode.IRETURN,
+                               Opcode.T_INT, 1);
+       primitiveTypes[4] = intType;
+
+       longType = new CtPrimitiveType("long", 'J', "java.lang.Long",
+                               "longValue", "()J", Opcode.LRETURN,
+                               Opcode.T_LONG, 2);
+       primitiveTypes[5] = longType;
+
+       floatType = new CtPrimitiveType("float", 'F', "java.lang.Float",
+                               "floatValue", "()F", Opcode.FRETURN,
+                               Opcode.T_FLOAT, 1);
+       primitiveTypes[6] = floatType;
+
+       doubleType = new CtPrimitiveType("double", 'D', "java.lang.Double",
+                               "doubleValue", "()D", Opcode.DRETURN,
+                               Opcode.T_DOUBLE, 2);
+       primitiveTypes[7] = doubleType;
+
+       voidType = new CtPrimitiveType("void", 'V', "java.lang.Void",
+                               null, null, Opcode.RETURN, 0, 0);
+       primitiveTypes[8] = voidType;
+    }
+
+    protected CtClass(String name) {
+       qualifiedName = name;
+    }
+
+    /**
+     * Returns a <code>ClassPool</code> for this class.
+     */
+    public ClassPool getClassPool() { return null; }
+
+    /**
+     * Returns a class file for this class.
+     *
+     * <p>This method is not available if <code>isFrozen()</code>
+     * is true.
+     */
+    public ClassFile getClassFile() {
+       checkModify();
+       return getClassFile2();
+    }
+
+    /**
+     * Undocumented method.  Do not use; internal-use only.
+     */
+    public ClassFile getClassFile2() { return null; }
+
+    /**
+     * Returns true if the definition of the class has been modified.
+     */
+    public boolean isModified() { return false; }
+
+    /**
+     * Returns true if the class has been loaded and thus it cannot be
+     * modified any more.
+     */
+    public boolean isFrozen() { return true; }
+
+    void checkModify() throws RuntimeException {
+       if (isFrozen())
+           throw new RuntimeException("the class is frozen");
+
+       // isModified() must return true after this method is invoked.
+    }
+
+    /**
+     * Defrosts the class so that the class can be modified.
+     *
+     * To avoid changes that are never reflected,
+     * the class is frozen to be unmodifiable if it is loaded or
+     * written out.  This method should be called only in a case
+     * that the class will be reloaded or written out later again.
+     */
+    public void defrost() {
+       throw new RuntimeException("cannot defrost " + getName());
+    }
+
+    /**
+     * Returns <code>true</code> if this object represents a primitive
+     * Java type: boolean, byte, char, short, int, long, float, double,
+     * or void.
+     */
+    public boolean isPrimitive() { return false; }
+
+    /**
+     * Returns <code>true</code> if this object represents an array type.
+     */
+    public boolean isArray() {
+       return false;
+    }
+
+    /**
+     * If this object represents an array, this method returns the component
+     * type of the array.  Otherwise, it returns <code>null</code>.
+     */
+    public CtClass getComponentType() throws NotFoundException {
+       return null;
+    }
+
+    /**
+     * Returns <code>true</code> if this class extends or implements
+     * <code>clazz</code>.  It also returns <code>true</code> if
+     * this class is the same as <code>clazz<code>.
+     */
+    public boolean subtypeOf(CtClass clazz) throws NotFoundException {
+       return this == clazz || getName().equals(clazz.getName());
+    }
+
+    /**
+     * Obtains the fully-qualified name of the class.
+     */
+    public String getName() { return qualifiedName; }
+
+    /**
+     * Obtains the not-qualified class name.
+     */
+    public final String getSimpleName() {
+       String qname = qualifiedName;
+       int index = qname.lastIndexOf('.');
+       if (index < 0)
+           return qname;
+       else
+           return qname.substring(index + 1);
+    }
+
+    /**
+     * Obtains the package name.  It may be <code>null</code>.
+     */
+    public final String getPackageName() {
+       String qname = qualifiedName;
+       int index = qname.lastIndexOf('.');
+       if (index < 0)
+           return null;
+       else
+           return qname.substring(0, index);
+    }
+
+    /**
+     * Sets the class name
+     *
+     * @param name     fully-qualified name
+     */
+    public void setName(String name) {
+       checkModify();
+       if (name != null)
+           qualifiedName = name;
+    }
+
+    /**
+     * Substitutes <code>newName</code> for all occurrences of a class
+     * name <code>oldName</code> in the class file.
+     *
+     * @param oldName          replaced class name
+     * @param newName          substituted class name
+     */
+    public void replaceClassName(String oldname, String newname) {
+       checkModify();
+    }
+
+    /**
+     * Changes class names appearing in the class file according to the
+     * given <code>map</code>.
+     *
+     * <p>All the class names appearing in the class file are tested
+     * with <code>map</code> to determine whether each class name is
+     * replaced or not.  Thus this method can be used for collecting
+     * all the class names in the class file.  To do that, first define
+     * a subclass of <code>ClassMap</code> so that <code>get()</code>
+     * records all the given parameters.  Then, make an instance of
+     * that subclass as an empty hash-table.  Finally, pass that instance
+     * to this method.  After this method finishes, that instance would
+     * contain all the class names appearing in the class file.
+     *
+     * @param map      the hashtable associating replaced class names
+     *                 with substituted names.
+     */
+    public void replaceClassName(ClassMap map) {
+       checkModify();
+    }
+
+    /**
+     * Returns a collection of the names of all the classes
+     * referenced in this class.
+     * That collection includes the name of this class.
+     *
+     * <p>This method may return <code>null</code>.
+     */
+    public Collection getRefClasses() {
+       ClassFile cf = getClassFile2();
+       if (cf != null) {
+           ClassMap cm = new ClassMap() {
+                   public void put(String oldname, String newname) {
+                       put0(oldname, newname);
+                   }
+
+                   public Object get(Object jvmClassName) {
+                       String n = toJavaName((String)jvmClassName);
+                       put0(n, n);
+                       return null;
+                   }
+
+                   public void fix(String name) {}
+               };
+           cf.renameClass(cm);
+           return cm.values();
+       }
+       else
+           return null;
+    }
+
+    /**
+     * Determines whether this object represents a class or an interface.
+     * It returns <code>true</code> if this object represents an interface.
+     */
+    public boolean isInterface() {
+       return false;
+    }
+
+    /**
+     * Returns the modifiers for this class, encoded in an integer.
+     * For decoding, use <code>javassist.Modifier</code>.
+     *
+     * @see Modifier
+     */
+    public int getModifiers() {
+       return 0;
+    }
+
+    /**
+     * Sets the modifiers.
+     *
+     * @param mod      modifiers encoded by
+     *                 <code>javassist.Modifier</code>
+     * @see Modifier
+     */
+    public void setModifiers(int mod) {
+       checkModify();
+    }
+
+    /**
+     * Determines whether the class directly or indirectly extends
+     * the given class.  If this class extends a class A and
+     * the class A extends a class B, then subclassof(B) returns true.
+     *
+     * <p>This method returns true if the given class is identical to
+     * the class represented by this object.
+     */
+    public boolean subclassOf(CtClass superclass) {
+       return false;
+    }
+
+    /**
+     * Obtains the class object representing the superclass of the
+     * class.
+     * It returns null if the class is <code>java.lang.Object</code>.
+     */
+    public CtClass getSuperclass() throws NotFoundException {
+       return null;
+    }
+
+    /**
+     * Changes a super class.
+     */
+    public void setSuperclass(CtClass clazz) throws CannotCompileException {
+       checkModify();
+    }
+
+    /**
+     * Obtains the class objects representing the interfaces of the
+     * class.
+     */
+    public CtClass[] getInterfaces() throws NotFoundException {
+       return new CtClass[0];
+    }
+
+    /**
+     * Sets interfaces.
+     *
+     * @param list             a list of the <code>CtClass</code> objects
+     *                         representing interfaces, or
+     *                         <code>null</code> if the class implements
+     *                         no interfaces.
+     */
+    public void setInterfaces(CtClass[] list) {
+       checkModify();
+    }
+
+    /**
+     * Adds an interface.
+     *
+     * @param anInterface      the added interface.
+     */
+    public void addInterface(CtClass anInterface) {
+       checkModify();
+    }
+
+    /**
+     * Returns an array containing <code>CtField</code> objects
+     * representing all the public fields of the class.
+     * That array includes public fields inherited from the
+     * superclasses.
+     */
+    public CtField[] getFields() { return new CtField[0]; }
+
+    /**
+     * Returns the field with the specified name.  The returned field
+     * may be a private field declared in a super class or interface.
+     */
+    public CtField getField(String name) throws NotFoundException {
+       throw new NotFoundException(name);
+    }
+
+    /**
+     * Gets all the fields declared in the class.  The inherited fields
+     * are not included.
+     *
+     * <p>Note: the result does not include inherited fields.
+     */
+    public CtField[] getDeclaredFields() { return new CtField[0]; }
+
+    /**
+     * Retrieves the field with the specified name among the fields
+     * declared in the class.
+     *
+     * <p>Note: this method does not search the superclasses.
+     */
+    public CtField getDeclaredField(String name) throws NotFoundException {
+       throw new NotFoundException(name);
+    }
+
+    /**
+     * Gets all the constructors and methods declared in the class.
+     */
+    public CtBehavior[] getDeclaredBehaviors() {
+       return new CtBehavior[0];
+    }
+
+    /**
+     * Returns an array containing <code>CtConstructor</code> objects
+     * representing all the public constructors of the class.
+     */
+    public CtConstructor[] getConstructors() {
+       return new CtConstructor[0];
+    }
+
+    /**
+     * Returns the constructor with the given signature,
+     * which is represented by a character string
+     * called method descriptor.
+     * For details of the method descriptor, see the JVM specification
+     * or <code>javassist.bytecode.Descriptor</code>.
+     *
+     * @param name     method name
+     * @param desc     method descriptor
+     * @see javassist.bytecode.Descriptor
+     */
+    public CtConstructor getConstructor(String desc)
+       throws NotFoundException
+    {
+       throw new NotFoundException("no such a constructor");
+    }
+
+    /**
+     * Gets all the constructors declared in the class.
+     *
+     * @see javassist.CtConstructor
+     */
+    public CtConstructor[] getDeclaredConstructors() {
+       return new CtConstructor[0];
+    }
+
+    /**
+     * Returns a constructor receiving the specified parameters.
+     *
+     * @param params   parameter types.
+     */
+    public CtConstructor getDeclaredConstructor(CtClass[] params)
+       throws NotFoundException
+    {
+       String desc = Descriptor.ofConstructor(params);
+       return getConstructor(desc);
+    }
+
+    /**
+     * Gets the class initializer (static constructor)
+     * declared in the class.
+     * This method returns <code>null</code> if
+     * no class initializer is not declared.
+     *
+     * @see javassist.CtConstructor
+     */
+    public CtConstructor getClassInitializer() {
+       return null;
+    }
+
+    /**
+     * Returns an array containing <code>CtMethod</code> objects
+     * representing all the public methods of the class.
+     * That array includes public methods inherited from the
+     * superclasses.
+     */
+    public CtMethod[] getMethods() {
+       return new CtMethod[0];
+    }
+
+    /**
+     * Returns the method with the given name and signature.
+     * The returned method may be declared in a super class.
+     * The method signature is represented by a character string
+     * called method descriptor,
+     * which is defined in the JVM specification.
+     *
+     * @param name     method name
+     * @param desc     method descriptor
+     * @see javassist.bytecode.Descriptor
+     */
+    public CtMethod getMethod(String name, String desc)
+       throws NotFoundException
+    {
+       throw new NotFoundException(name);
+    }
+
+    /**
+     * Gets all methods declared in the class.  The inherited methods
+     * are not included.
+     *
+     * @see javassist.CtMethod
+     */
+    public CtMethod[] getDeclaredMethods() {
+       return new CtMethod[0];
+    }
+
+    /**
+     * Retrieves the method with the specified name and parameter types
+     * among the methods declared in the class.
+     *
+     * <p>Note: this method does not search the superclasses.
+     *
+     * @param name             method name
+     * @param params           parameter types
+     * @see javassist.CtMethod
+     */
+    public CtMethod getDeclaredMethod(String name, CtClass[] params)
+       throws NotFoundException
+    {
+       throw new NotFoundException(name);
+    }
+
+    /**
+     * Retrieves the method with the specified name among the methods
+     * declared in the class.  If there are multiple methods with
+     * the specified name, then this method returns one of them.
+     *
+     * <p>Note: this method does not search the superclasses.
+     *
+     * @see javassist.CtMethod
+     */
+    public CtMethod getDeclaredMethod(String name) throws NotFoundException {
+       throw new NotFoundException(name);
+    }
+
+    /**
+     * Adds a constructor.
+     */
+    public void addConstructor(CtConstructor c)
+       throws CannotCompileException
+    {
+       checkModify();
+    }
+
+    /**
+     * Adds a method.
+     */
+    public void addMethod(CtMethod m) throws CannotCompileException {
+       checkModify();
+    }
+
+    /**
+     * Adds a field.
+     *
+     * <p>The <code>CtField</code> belonging to another
+     * <code>CtClass</code> cannot be directly added to this class.
+     * Only a field created for this class can be added.
+     *
+     * @see javassist.CtField#CtField(CtField,CtClass)
+     */
+    public void addField(CtField f) throws CannotCompileException {
+       addField(f, (CtField.Initializer)null);
+    }
+
+    /**
+     * Adds a field with an initial value.
+     *
+     * <p>The <code>CtField</code> belonging to another
+     * <code>CtClass</code> cannot be directly added to this class.
+     * Only a field created for this class can be added.
+     *
+     * <p>The initial value is given as an expression written in Java.
+     * Any regular Java expression can be used for specifying the initial
+     * value.  The followings are examples.
+     *
+     * <ul><pre>
+     * cc.addField(f, "0")               // the initial value is 0.
+     * cc.addField(f, "i + 1")           // i + 1.
+     * cc.addField(f, "new Point()");    // a Point object.
+     * </pre></ul>
+     *
+     * <p>Here, the type of variable <code>cc</code> is <code>CtClass</code>.
+     * The type of <code>f</code> is <code>CtField</code>.
+     *
+     * @param init     an expression for the initial value.
+     *
+     * @see javassist.CtField.Initializer#byExpr(String)
+     * @see javassist.CtField#CtField(CtField,CtClass)
+     */
+    public void addField(CtField f, String init)
+       throws CannotCompileException
+    {
+       checkModify();
+    }
+
+    /**
+     * Adds a field with an initial value.
+     *
+     * <p>The <code>CtField</code> belonging to another
+     * <code>CtClass</code> cannot be directly added to this class.
+     * Only a field created for this class can be added.
+     *
+     * <p>For example,
+     *
+     * <ul><pre>
+     * CtClass cc = ...;
+     * addField(new CtField(CtClass.intType, "i", cc),
+     *          CtField.Initializer.constant(1));
+     * </pre></ul>
+     *
+     * <p>This code adds an <code>int</code> field named "i".  The
+     * initial value of this field is 1.
+     *
+     * @param init     specifies the initial value of the field.
+     *
+     * @see javassist.CtField#CtField(CtField,CtClass)
+     */
+    public void addField(CtField f, CtField.Initializer init)
+       throws CannotCompileException
+    {
+       checkModify();
+    }
+
+    /**
+     * Obtains an attribute with the given name.
+     * If that attribute is not found in the class file, this
+     * method returns null.
+     *
+     * @param name             attribute name
+     */
+    public byte[] getAttribute(String name) {
+       return null;
+    }
+
+    /**
+     * Adds a named attribute.
+     * An arbitrary data (smaller than 64Kb) can be saved in the class
+     * file.  Some attribute name are reserved by the JVM.
+     * The attributes with the non-reserved names are ignored when a
+     * class file is loaded into the JVM.
+     * If there is already an attribute with
+     * the same name, this method substitutes the new one for it.
+     *
+     * @param name     attribute name
+     * @param data     attribute value
+     */
+    public void setAttribute(String name, byte[] data) {
+       checkModify();
+    }
+
+    /**
+     * Applies the given converter to all methods and constructors
+     * declared in the class.  This method calls <code>instrument()</code>
+     * on every <code>CtMethod</code> and <code>CtConstructor</code> object
+     * in the class.
+     *
+     * @param converter                specifies how to modify.
+     */
+    public void instrument(CodeConverter converter)
+       throws CannotCompileException
+    {
+       checkModify();
+    }
+
+    /**
+     * Modifies the bodies of all methods and constructors
+     * declared in the class.  This method calls <code>instrument()</code>
+     * on every <code>CtMethod</code> and <code>CtConstructor</code> object
+     * in the class.
+     *
+     * @param editor           specifies how to modify.
+     */
+    public void instrument(ExprEditor editor)
+       throws CannotCompileException
+    {
+       checkModify();
+    }
+
+    /**
+     * Converts this class to a <code>java.lang.Class</code> object.
+     * Once this method is called, further modifications are not
+     * possible any more.
+     *
+     * <p>This method is equivalent to:
+     * <ul><pre>this.getClassPool().writeAsClass(this.getName())</pre></ul>
+     *
+     * <p>See the description of <code>ClassPool.writeAsClass()</code>
+     * before you use this method.
+     *
+     * @see javassist.ClassPool#writeAsClass(String)
+     * @see javassist.ClassPool#forName(String)
+     */
+    public Class toClass()
+       throws NotFoundException, IOException, CannotCompileException
+    {
+       return getClassPool2().writeAsClass(getName());
+    }
+
+    /**
+     * Converts this class to a class file.
+     * Once this method is called, further modifications are not
+     * possible any more.
+     *
+     * <p>This method is equivalent to:
+     * <ul><pre>this.getClassPool().write(this.getName())</pre></ul>
+     *
+     * @see javassist.ClassPool#write(String)
+     */
+    public byte[] toBytecode()
+       throws NotFoundException, IOException, CannotCompileException
+    {
+       return getClassPool2().write(getName());
+    }
+
+    /**
+     * Writes a class file represented by this <code>CtClass</code>
+     * object in the current directory.
+     * Once this method is called, further modifications are not
+     * possible any more.
+     *
+     * <p>This method is equivalent to:
+     * <ul><pre>this.getClassPool().writeFile(this.getName())</pre></ul>
+     *
+     * @see javassist.ClassPool#writeFile(String)
+     */
+    public void writeFile()
+       throws NotFoundException, IOException, CannotCompileException
+    {
+       getClassPool2().writeFile(getName());
+    }
+
+    private ClassPool getClassPool2() throws CannotCompileException {
+       ClassPool cp = getClassPool();
+       if (cp == null)
+           throw new CannotCompileException(
+                               "no ClassPool found. not a class?");
+       else
+           return cp;
+    }
+
+    /**
+     * Converts this class to a class file.
+     * Once this method is called, further modifications are not
+     * possible any more.
+     *
+     * <p>If this method is used to obtain a byte array representing
+     * the class file, <code>Translator.onWrite()</code> is never
+     * called on this class.  <code>ClassPool.write()</code> should
+     * be used.
+     *
+     * <p>This method dose not close the output stream in the end.
+     *
+     * @param out      the output stream that a class file is written to.
+     */
+    void toBytecode(DataOutputStream out)
+       throws CannotCompileException, IOException
+    {
+       throw new CannotCompileException("not a class");
+    }
+}
diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java
new file mode 100644 (file)
index 0000000..e08ed7b
--- /dev/null
@@ -0,0 +1,872 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.compiler.Javac;
+import javassist.compiler.CompileError;
+import javassist.expr.ExprEditor;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+
+/**
+ * Class types.
+ */
+class CtClassType extends CtClass {
+    protected ClassPool classPool;
+    protected boolean wasChanged;
+    protected boolean wasFrozen;
+    protected ClassFile classfile;
+
+    private CtField fieldsCache;
+    private CtConstructor constructorsCache;
+    private CtConstructor classInitializerCache;
+    private CtMethod methodsCache;
+
+    private FieldInitLink fieldInitializers;
+    private Hashtable hiddenMethods;   // must be synchronous
+    private int uniqueNumberSeed;
+
+    CtClassType(String name, ClassPool cp) {
+       super(name);
+       classPool = cp;
+       wasChanged = wasFrozen = false;
+       classfile = null;
+       fieldInitializers = null;
+       hiddenMethods = null;
+       uniqueNumberSeed = 0;
+       eraseCache();
+    }
+
+    protected void eraseCache() {
+       fieldsCache = null;
+       constructorsCache = null;
+       classInitializerCache = null;
+       methodsCache = null;
+    }
+
+    public ClassFile getClassFile2() {
+       if (classfile != null)
+           return classfile;
+
+       try {
+           byte[] b = classPool.readSource(getName());
+           DataInputStream dis
+               = new DataInputStream(new ByteArrayInputStream(b));
+           return (classfile = new ClassFile(dis));
+       }
+       catch (NotFoundException e) {
+           throw new RuntimeException(e.toString());
+       }
+       catch (IOException e) {
+           throw new RuntimeException(e.toString());
+       }
+       catch (CannotCompileException e) {
+           throw new RuntimeException(e.toString());
+       }
+    }
+
+    public ClassPool getClassPool() { return classPool; }
+
+    public boolean isModified() { return wasChanged; }
+
+    public boolean isFrozen() { return wasFrozen; }
+
+    void checkModify() throws RuntimeException {
+       super.checkModify();
+       wasChanged = true;
+    }
+
+    public void defrost() { wasFrozen = false; }
+
+    public boolean subtypeOf(CtClass clazz) throws NotFoundException {
+       int i;
+       String cname = clazz.getName();
+       if (this == clazz || getName().equals(cname))
+           return true;
+
+       ClassFile file = getClassFile2();
+       String supername = file.getSuperclass();
+       if (supername != null && supername.equals(cname))
+           return true;
+
+       String[] ifs = file.getInterfaces();
+       int num = ifs.length;
+       for (i = 0; i < num; ++i)
+           if (ifs[i].equals(cname))
+               return true;
+
+       if (supername != null && classPool.get(supername).subtypeOf(clazz))
+           return true;
+
+       for (i = 0; i < num; ++i)
+           if (classPool.get(ifs[i]).subtypeOf(clazz))
+               return true;
+
+       return false;
+    }
+
+    public void setName(String name) throws RuntimeException {
+       String oldname = getName();
+       if (name.equals(oldname))
+           return;
+
+       classPool.checkNotFrozen(name,
+                                "the class with the new name is frozen");
+       ClassFile cf = getClassFile2();
+       super.setName(name);
+       cf.setName(name);
+       eraseCache();
+       classPool.classNameChanged(oldname, this);
+    }
+
+    public void replaceClassName(ClassMap classnames)
+       throws RuntimeException 
+    {
+       String oldClassName = getName();
+       String newClassName
+           = (String)classnames.get(Descriptor.toJvmName(oldClassName));
+       if (newClassName != null) {
+           newClassName = Descriptor.toJavaName(newClassName);
+           classPool.checkNotFrozen(newClassName,
+                       "the class " + newClassName + " is frozen");
+       }
+
+       super.replaceClassName(classnames);
+       ClassFile cf = getClassFile2();
+       cf.renameClass(classnames);
+       eraseCache();
+
+       if (newClassName != null) {
+           super.setName(newClassName);
+           classPool.classNameChanged(oldClassName, this);
+       }
+    }
+
+    public void replaceClassName(String oldname, String newname)
+       throws RuntimeException 
+    {
+       String thisname = getName();
+       if (thisname.equals(oldname))
+           setName(newname);
+       else {
+           super.replaceClassName(oldname, newname);
+           getClassFile2().renameClass(oldname, newname);
+           eraseCache();
+       }
+    }
+
+    public boolean isInterface() {
+       return Modifier.isInterface(getModifiers());
+    }
+
+    public int getModifiers() {
+       int acc = getClassFile2().getAccessFlags();
+       acc = AccessFlag.clear(acc, AccessFlag.SUPER);
+       return AccessFlag.toModifier(acc);
+    }
+
+    public void setModifiers(int mod) {
+       checkModify();
+       int acc = AccessFlag.of(mod) | AccessFlag.SUPER;
+       getClassFile2().setAccessFlags(acc);
+    }
+
+    public boolean subclassOf(CtClass superclass) {
+       CtClass curr = this;
+       try {
+           while (curr != null) {
+               if (curr == superclass)
+                   return true;
+
+               curr = curr.getSuperclass();
+           }
+       }
+       catch (Exception ignored) {}
+       return false;
+    }
+
+    public CtClass getSuperclass() throws NotFoundException {
+       String supername = getClassFile2().getSuperclass();
+       if (supername == null)
+           return null;
+       else
+           return classPool.get(supername);
+    }
+
+    public void setSuperclass(CtClass clazz) throws CannotCompileException {
+       checkModify();
+       getClassFile2().setSuperclass(clazz.getName());
+    }
+
+    public CtClass[] getInterfaces() throws NotFoundException {
+       String[] ifs = getClassFile2().getInterfaces();
+       int num = ifs.length;
+       CtClass[] ifc = new CtClass[num];
+       for (int i = 0; i < num; ++i)
+           ifc[i] = classPool.get(ifs[i]);
+
+       return ifc;
+    }
+
+    public void setInterfaces(CtClass[] list) {
+       checkModify();
+       String[] ifs;
+       if (list == null)
+           ifs = new String[0];
+       else {
+           int num = list.length;
+           ifs = new String[num];
+           for (int i = 0; i < num; ++i)
+               ifs[i] = list[i].getName();
+       }
+
+       getClassFile2().setInterfaces(ifs);
+    }
+
+    public void addInterface(CtClass anInterface) {
+       checkModify();
+       if (anInterface != null)
+           getClassFile2().addInterface(anInterface.getName());
+    }
+
+    public CtField[] getFields() {
+       ArrayList alist = new ArrayList();
+       getFields(alist, this);
+       return (CtField[])alist.toArray(new CtField[alist.size()]);
+    }
+
+    private static void getFields(ArrayList alist, CtClass cc) {
+       int i, num;
+       if (cc == null)
+           return;
+
+       try {
+           getFields(alist, cc.getSuperclass());
+       }
+       catch (NotFoundException e) {}
+
+       try {
+           CtClass[] ifs = cc.getInterfaces();
+           num = ifs.length;
+           for (i = 0; i < num; ++i)
+               getFields(alist, ifs[i]);
+       }
+       catch (NotFoundException e) {}
+
+       CtField cf = ((CtClassType)cc).getFieldsCache();
+       while (cf != null) {
+           if (Modifier.isPublic(cf.getModifiers()))
+               alist.add(cf);
+
+           cf = cf.next;
+       }
+    }
+
+    public CtField getField(String name) throws NotFoundException {
+       try {
+           return getDeclaredField(name);
+       }
+       catch (NotFoundException e) {}
+
+       try {
+           CtClass[] ifs = getInterfaces();
+           int num = ifs.length;
+           for (int i = 0; i < num; ++i)
+               try {
+                   return ifs[i].getField(name);
+               }
+               catch (NotFoundException e) {}
+       }
+       catch (NotFoundException e) {}
+
+       try {
+           CtClass s = getSuperclass();
+           if (s != null)
+               return s.getField(name);
+       }
+       catch (NotFoundException e) {}
+
+       throw new NotFoundException(name);
+    }
+
+    public CtField[] getDeclaredFields() {
+       CtField cf = getFieldsCache();
+       int num = CtField.count(cf);
+       CtField[] cfs = new CtField[num];
+       int i = 0;
+       while (cf != null) {
+           cfs[i++] = cf;
+           cf = cf.next;
+       }
+
+       return cfs;
+    }
+
+    protected CtField getFieldsCache() {
+       if (fieldsCache == null) {
+           List list = getClassFile2().getFields();
+           int n = list.size();
+           for (int i = 0; i < n; ++i) {
+               FieldInfo finfo = (FieldInfo)list.get(i);
+               fieldsCache = CtField.append(fieldsCache,
+                                            new CtField(finfo, this));
+           }
+       }
+
+       return fieldsCache;
+    }
+
+    public CtField getDeclaredField(String name) throws NotFoundException {
+       CtField cf = getFieldsCache();
+       while (cf != null) {
+           if (cf.getName().equals(name))
+               return cf;
+
+           cf = cf.next;
+       }
+
+       throw new NotFoundException(name);
+    }
+
+    public CtBehavior[] getDeclaredBehaviors() {
+       CtConstructor cc = getConstructorsCache();
+       CtMethod cm = getMethodsCache();
+       int num = CtMethod.count(cm) + CtConstructor.count(cc);
+       CtBehavior[] cb = new CtBehavior[num];
+       int i = 0;
+       while (cc != null) {
+           cb[i++] = cc;
+           cc = cc.next;
+       }
+
+       while (cm != null) {
+           cb[i++] = cm;
+           cm = cm.next;
+       }
+       
+       return cb;
+    }
+
+    public CtConstructor[] getConstructors() {
+       CtConstructor[] cons = getDeclaredConstructors();
+       if (cons.length == 0)
+           return cons;
+
+       int n = 0;
+       int i = cons.length;
+       while (--i >= 0)
+           if (Modifier.isPublic(cons[i].getModifiers()))
+               ++n;
+
+       CtConstructor[] result = new CtConstructor[n];
+       n = 0;
+       i = cons.length;
+       while (--i >= 0) {
+           CtConstructor c = cons[i];
+           if (Modifier.isPublic(c.getModifiers()))
+               result[n++] = c;
+       }
+
+       return result;
+    }
+
+    public CtConstructor getConstructor(String desc)
+       throws NotFoundException
+    {
+       CtConstructor cc = getConstructorsCache();
+       while (cc != null) {
+           if (cc.getMethodInfo2().getDescriptor().equals(desc))
+               return cc;
+
+           cc = cc.next;
+       }
+
+       return super.getConstructor(desc);
+    }
+
+    public CtConstructor[] getDeclaredConstructors() {
+       CtConstructor cc = getConstructorsCache();
+       int num = CtConstructor.count(cc);
+       CtConstructor[] ccs = new CtConstructor[num];
+       int i = 0;
+       while (cc != null) {
+           ccs[i++] = cc;
+           cc = cc.next;
+       }
+       
+       return ccs;
+    }
+
+    protected CtConstructor getConstructorsCache() {
+       if (constructorsCache == null) {
+           List list = getClassFile2().getMethods();
+           int n = list.size();
+           for (int i = 0; i < n; ++i) {
+               MethodInfo minfo = (MethodInfo)list.get(i);
+               if (minfo.isConstructor())
+                   constructorsCache
+                       = CtConstructor.append(constructorsCache,
+                                           new CtConstructor(minfo, this));
+           }
+       }
+
+       return constructorsCache;
+    }
+
+    public CtConstructor getClassInitializer() {
+       if (classInitializerCache == null) {
+           List list = getClassFile2().getMethods();
+           int n = list.size();
+           for (int i = 0; i < n; ++i) {
+               MethodInfo minfo = (MethodInfo)list.get(i);
+               if (minfo.isStaticInitializer()) {
+                   classInitializerCache = new CtConstructor(minfo, this);
+                   break;
+               }
+           }
+       }
+
+       return classInitializerCache;
+    }
+
+    public CtMethod[] getMethods() {
+       HashMap h = new HashMap();
+       getMethods0(h, this);
+       return (CtMethod[])h.values().toArray(new CtMethod[0]);
+    }
+
+    private static void getMethods0(HashMap h, CtClass cc) {
+       try {
+           CtClass[] ifs = cc.getInterfaces();
+           int size = ifs.length;
+           for (int i = 0; i < size; ++i)
+               getMethods0(h, ifs[i]);
+       }
+       catch (NotFoundException e) {}
+
+       try {
+           CtClass s = cc.getSuperclass();
+           if (s != null)
+               getMethods0(h, s);
+       }
+       catch (NotFoundException e) {}
+
+       if (cc instanceof CtClassType) {
+           CtMethod cm = ((CtClassType)cc).getMethodsCache();
+           while (cm != null) {
+               if (Modifier.isPublic(cm.getModifiers()))
+                   h.put(cm, cm);
+
+               cm = cm.next;
+           }
+       }
+    }
+
+    public CtMethod getMethod(String name, String desc)
+       throws NotFoundException
+    {
+       CtMethod m = getMethod0(this, name, desc);
+       if (m != null)
+           return m;
+       else
+           throw new NotFoundException(name + "(..) is not found in "
+                                       + getName());
+    }
+
+    private static CtMethod getMethod0(CtClass cc,
+                                      String name, String desc) {
+       if (cc instanceof CtClassType) {
+           CtMethod cm = ((CtClassType)cc).getMethodsCache();
+           while (cm != null) {
+               if (cm.getName().equals(name)
+                   && cm.getMethodInfo2().getDescriptor().equals(desc))
+                   return cm;
+
+               cm = cm.next;
+           }
+       }
+
+       try {
+           CtClass s = cc.getSuperclass();
+           if (s != null) {
+               CtMethod m = getMethod0(s, name, desc);
+               if (m != null)
+                   return m;
+           }
+       }
+       catch (NotFoundException e) {}
+
+       try {
+           CtClass[] ifs = cc.getInterfaces();
+           int size = ifs.length;
+           for (int i = 0; i < size; ++i) {
+               CtMethod m = getMethod0(ifs[i], name, desc);
+               if (m != null)
+                   return m;
+           }
+       }
+       catch (NotFoundException e) {}
+       return null;
+    }
+
+    public CtMethod[] getDeclaredMethods() {
+       CtMethod cm = getMethodsCache();
+       int num = CtMethod.count(cm);
+       CtMethod[] cms = new CtMethod[num];
+       int i = 0;
+       while (cm != null) {
+           cms[i++] = cm;
+           cm = cm.next;
+       }
+       
+       return cms;
+    }
+
+    public CtMethod getDeclaredMethod(String name) throws NotFoundException {
+       CtMethod m = getMethodsCache();
+       while (m != null) {
+           if (m.getName().equals(name))
+               return m;
+
+           m = m.next;
+       }
+
+       throw new NotFoundException(name + "(..) is not found in "
+                                   + getName());
+    }
+
+    public CtMethod getDeclaredMethod(String name, CtClass[] params)
+       throws NotFoundException
+    {
+       String desc = Descriptor.ofParameters(params);
+       CtMethod m = getMethodsCache();
+       while (m != null) {
+           if (m.getName().equals(name)
+               && m.getMethodInfo2().getDescriptor().startsWith(desc))
+               return m;
+
+           m = m.next;
+       }
+
+       throw new NotFoundException(name + "(..) is not found in "
+                                   + getName());
+    }
+
+    protected CtMethod getMethodsCache() {
+       if (methodsCache == null) {
+           List list = getClassFile2().getMethods();
+           int n = list.size();
+           for (int i = 0; i < n; ++i) {
+               MethodInfo minfo = (MethodInfo)list.get(i);
+               if (minfo.isMethod())
+                   methodsCache = CtMethod.append(methodsCache,
+                                               new CtMethod(minfo, this));
+           }
+       }
+
+       return methodsCache;
+    }
+
+    public void addField(CtField f, String init)
+       throws CannotCompileException
+    {
+       addField(f, CtField.Initializer.byExpr(init));
+    }
+
+    public void addField(CtField f, CtField.Initializer init)
+       throws CannotCompileException
+    {
+       checkModify();
+       if (f.getDeclaringClass() != this)
+           throw new CannotCompileException("cannot add");
+
+       if (init == null)
+           init = f.getInit();
+
+       getFieldsCache();
+       fieldsCache = CtField.append(fieldsCache, f);
+       getClassFile2().addField(f.getFieldInfo2());
+
+       if (init != null) {
+           FieldInitLink fil = new FieldInitLink(f, init);
+           FieldInitLink link = fieldInitializers;
+           if (link == null)
+               fieldInitializers = fil;
+           else {
+               while (link.next != null)
+                   link = link.next;
+
+               link.next = fil;
+           }
+       }
+    }
+
+    public void addConstructor(CtConstructor c)
+       throws CannotCompileException
+    {
+       checkModify();
+       if (c.getDeclaringClass() != this)
+           throw new CannotCompileException("cannot add");
+
+       getConstructorsCache();
+       constructorsCache = CtConstructor.append(constructorsCache, c);
+       getClassFile2().addMethod(c.getMethodInfo2());
+    }
+
+    public void addMethod(CtMethod m) throws CannotCompileException {
+       checkModify();
+       if (m.getDeclaringClass() != this)
+           throw new CannotCompileException("cannot add");
+
+       getMethodsCache();
+       methodsCache = CtMethod.append(methodsCache, m);
+       getClassFile2().addMethod(m.getMethodInfo2());
+       if ((m.getModifiers() & Modifier.ABSTRACT) != 0)
+           setModifiers(getModifiers() | Modifier.ABSTRACT);
+    }
+
+    public byte[] getAttribute(String name) {
+       AttributeInfo ai = getClassFile2().getAttribute(name);
+       if (ai == null)
+           return null;
+       else
+           return ai.get();
+    }
+
+    public void setAttribute(String name, byte[] data) {
+       checkModify();
+       ClassFile cf = getClassFile2();
+       cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data));
+    }
+
+    public void instrument(CodeConverter converter)
+       throws CannotCompileException
+    {
+       checkModify();
+       ClassFile cf = getClassFile2();
+       ConstPool cp = cf.getConstPool();
+       List list = cf.getMethods();
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           converter.doit(this, minfo, cp);
+       }
+    }
+
+    public void instrument(ExprEditor editor)
+       throws CannotCompileException
+    {
+       checkModify();
+       ClassFile cf = getClassFile2();
+       List list = cf.getMethods();
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           editor.doit(this, minfo);
+       }
+    }
+
+    void toBytecode(DataOutputStream out)
+       throws CannotCompileException, IOException
+    {
+       ClassFile cf = getClassFile2();
+       try {
+           modifyClassConstructor(cf);
+           modifyConstructors(cf);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+
+       wasFrozen = true;
+       try {
+           cf.write(out);
+           out.flush();
+       }
+       catch (IOException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    protected void modifyClassConstructor(ClassFile cf)
+       throws CannotCompileException, NotFoundException
+    {
+       Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
+       Javac jv = new Javac(code, this);
+       int stacksize = 0;
+       boolean none = true;
+       for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) {
+           CtField f = fi.field;
+           if (Modifier.isStatic(f.getModifiers())) {
+               none = false;
+               int s = fi.init.compileIfStatic(f.getType(), f.getName(),
+                                               code, jv);
+               if (stacksize < s)
+                   stacksize = s;
+           }
+       }
+
+       if (none)
+           return;     // no initializer for static fileds.
+
+       MethodInfo m = cf.getStaticInitializer();
+       if (m == null) {
+           code.add(Bytecode.RETURN);
+           code.setMaxStack(stacksize);
+           m = new MethodInfo(cf.getConstPool(),
+                              "<clinit>", "()V");
+           m.setAccessFlags(AccessFlag.STATIC);
+           m.setCodeAttribute(code.toCodeAttribute());
+           cf.addMethod(m);
+       }
+       else {
+           CodeAttribute codeAttr = m.getCodeAttribute();
+           if (codeAttr == null)
+               throw new CannotCompileException("empty <clinit>");
+
+           try {
+               CodeIterator it = codeAttr.iterator();
+               int pos = it.insertEx(code.get());
+               it.insert(code.getExceptionTable(), pos);
+               int maxstack = codeAttr.getMaxStack();
+               if (maxstack < stacksize)
+                   codeAttr.setMaxStack(stacksize);
+           }
+           catch (BadBytecode e) {
+               throw new CannotCompileException(e);
+           }
+       }
+    }
+
+    protected void modifyConstructors(ClassFile cf)
+       throws CannotCompileException, NotFoundException
+    {
+       if (fieldInitializers == null)
+           return;
+
+       ConstPool cp = cf.getConstPool();
+       List list = cf.getMethods();
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           if (minfo.isConstructor()) {
+               CodeAttribute codeAttr = minfo.getCodeAttribute();
+               if (codeAttr != null)
+                   try {
+                       Bytecode init = new Bytecode(cp, 0,
+                                            codeAttr.getMaxLocals());
+                       CtClass[] params
+                           = Descriptor.getParameterTypes(
+                                               minfo.getDescriptor(),
+                                               classPool);
+                       int stacksize = makeFieldInitializer(init, params);
+                       insertAuxInitializer(codeAttr, init, stacksize);
+                   }
+                   catch (BadBytecode e) {
+                       throw new CannotCompileException(e);
+                   }
+           }
+       }
+    }
+
+    private static void insertAuxInitializer(CodeAttribute codeAttr,
+                                            Bytecode initializer,
+                                            int stacksize)
+       throws BadBytecode
+    {
+       CodeIterator it = codeAttr.iterator();
+       int index = it.skipSuperConstructor();
+       if (index < 0) {
+           index = it.skipThisConstructor();
+           if (index >= 0)
+               return;         // this() is called.
+
+           // Neither this() or super() is called.
+       }
+
+       int pos = it.insertEx(initializer.get());
+       it.insert(initializer.getExceptionTable(), pos);
+       int maxstack = codeAttr.getMaxStack();
+       if (maxstack < stacksize)
+           codeAttr.setMaxStack(stacksize);
+    }
+
+    private int makeFieldInitializer(Bytecode code, CtClass[] parameters)
+       throws CannotCompileException, NotFoundException
+    {
+       int stacksize = 0;
+       Javac jv = new Javac(code, this);
+       try {
+           jv.recordParams(parameters, false);
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+
+       for (FieldInitLink fi = fieldInitializers; fi !=  null; fi = fi.next) {
+           CtField f = fi.field;
+           if (!Modifier.isStatic(f.getModifiers())) {
+               int s = fi.init.compile(f.getType(), f.getName(), code,
+                                       parameters, jv);
+               if (stacksize < s)
+                   stacksize = s;
+           }
+       }
+
+       return stacksize;
+    }
+
+    // Methods used by CtNewWrappedMethod
+
+    Hashtable getHiddenMethods() {
+       if (hiddenMethods == null)
+           hiddenMethods = new Hashtable();
+
+       return hiddenMethods;
+    }
+
+    int getUniqueNumber() { return uniqueNumberSeed++; }
+}
+
+class FieldInitLink {
+    FieldInitLink next;
+    CtField field;
+    CtField.Initializer init;
+
+    FieldInitLink(CtField f, CtField.Initializer i) {
+       next = null;
+       field = f;
+       init = i;
+    }
+}
diff --git a/src/main/javassist/CtConstructor.java b/src/main/javassist/CtConstructor.java
new file mode 100644 (file)
index 0000000..52024e7
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.compiler.Javac;
+import javassist.compiler.CompileError;
+import javassist.expr.ExprEditor;
+
+/* Some methods do nothing except calling the super's method.
+ * They might seem redundant but they are necessary so that javadoc
+ * includes the description of those methods in the page of this class.
+ */
+
+/**
+ * An instance of CtConstructor represents a constructor.
+ * It may represent a static constructor
+ * (class initializer).  To distinguish a constructor and a class
+ * initializer, call <code>isClassInitializer()</code>.
+ *
+ * @see CtClass#getDeclaredConstructors()
+ * @see CtClass#getClassInitializer()
+ * @see CtNewConstructor
+ */
+public final class CtConstructor extends CtBehavior {
+    protected CtConstructor next;
+
+    protected CtConstructor(MethodInfo minfo, CtClass declaring) {
+       super(declaring, minfo);
+       next = null;
+    }
+
+    /**
+     * Creates a constructor with no constructor body.
+     * The created constructor
+     * must be added to a class with <code>CtClass.addConstructor()</code>.
+     *
+     * <p>The created constructor does not include a constructor body,
+     * must be specified with <code>setBody()</code>.
+     *
+     * @param declaring                the class to which the created method is added.
+     * @param parameters       a list of the parameter types
+     *
+     * @see CtClass#addConstructor(CtConstructor)
+     * @see CtConstructor#setBody(String)
+     * @see CtConstructor#setBody(CtConstructor,ClassMap)
+     */
+    public CtConstructor(CtClass[] parameters, CtClass declaring) {
+       this((MethodInfo)null, declaring);
+       ConstPool cp = declaring.getClassFile2().getConstPool();
+       String desc = Descriptor.ofConstructor(parameters);
+       methodInfo = new MethodInfo(cp, "<init>", desc);
+       setModifiers(Modifier.PUBLIC);
+    }
+
+    /**
+     * Creates a copy of a <code>CtConstructor</code> object.
+     * The created constructor must be
+     * added to a class with <code>CtClass.addConstructor()</code>.
+     *
+     * <p>All occurrences of class names in the created constructor
+     * are replaced with names specified by
+     * <code>map</code> if <code>map</code> is not <code>null</code>.
+     *
+     * <p>By default, all the occurrences of the names of the class
+     * declaring <code>src</code> and the superclass are replaced
+     * with the name of the class and the superclass that
+     * the created constructor is added to.
+     * This is done whichever <code>map</code> is null or not.
+     * To prevent this replacement, call <code>ClassMap.fix()</code>.
+     *
+     * <p><b>Note:</b> if the <code>.class</code> notation (for example,
+     * <code>String.class</code>) is included in an expression, the
+     * Javac compiler may produce a helper method.
+     * Since this constructor never
+     * copies this helper method, the programmers have the responsiblity of
+     * copying it.  Otherwise, use <code>Class.forName()</code> in the
+     * expression.
+     *
+     * @param src      the source method.
+     * @param declaring    the class to which the created method is added.
+     * @param map      the hashtable associating original class names
+     *                 with substituted names.
+     *                 It can be <code>null</code>.
+     *
+     * @see CtClass#addConstructor(CtConstructor)
+     * @see ClassMap#fix(String)
+     */
+    public CtConstructor(CtConstructor src, CtClass declaring, ClassMap map)
+       throws CannotCompileException
+    {
+       this((MethodInfo)null, declaring);
+       MethodInfo srcInfo = src.methodInfo;
+       CtClass srcClass = src.getDeclaringClass();
+       ConstPool cp = declaring.getClassFile2().getConstPool();
+       if (map == null)
+           map = new ClassMap();
+
+       map.put(srcClass.getName(), declaring.getName());
+       try {
+           boolean patch = false;
+           CtClass srcSuper = srcClass.getSuperclass();
+           String destSuperName = declaring.getSuperclass().getName();
+           if (srcSuper != null) {
+               String srcSuperName = srcSuper.getName();
+               if (!srcSuperName.equals(destSuperName))
+                   if (srcSuperName.equals(CtClass.javaLangObject))
+                       patch = true;
+                   else
+                       map.put(srcSuperName, destSuperName);
+           }
+
+           methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map);
+           if (patch)
+               methodInfo.setSuperclass(destSuperName);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+       catch (BadBytecode e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    static CtConstructor append(CtConstructor list, CtConstructor tail) {
+       tail.next = null;
+       if (list == null)
+           return tail;
+       else {
+           CtConstructor lst = list;
+           while (lst.next != null)
+               lst = lst.next;
+
+           lst.next = tail;
+           return list;
+       }
+    }
+
+    static int count(CtConstructor m) {
+       int n = 0;
+       while (m != null) {
+           ++n;
+           m = m.next;
+       }
+
+       return n;
+    }
+
+    /**
+     * Returns the MethodInfo representing the constructor in the
+     * class file.
+     */
+    public MethodInfo getMethodInfo() {
+       return super.getMethodInfo();
+    }
+
+    /**
+     * Returns true if this object represents a constructor.
+     */
+    public boolean isConstructor() {
+       return methodInfo.isConstructor();
+    }
+
+    /**
+     * Returns true if this object represents a static initializer.
+     */
+    public boolean isClassInitializer() {
+       return methodInfo.isStaticInitializer();
+    }
+
+    /**
+     * Obtains the encoded modifiers of the constructor.
+     *
+     * @return         modifiers encoded with
+     *                 <code>javassist.Modifier</code>.
+     * @see Modifier
+     */
+    public int getModifiers() {
+       return super.getModifiers();
+    }
+
+    /**
+     * Sets the encoded modifiers of the constructor.
+     *
+     * @see Modifier
+     */
+    public void setModifiers(int mod) {
+       super.setModifiers(mod);
+    }
+
+    /**
+     * Obtains the name of this constructor.
+     * It is the same as the simple name of the class declaring this
+     * constructor.  If this object represents a class initializer,
+     * then this method returns <code>"&lt;clinit&gt;"</code>.
+     */
+    public String getName() {
+       if (methodInfo.isStaticInitializer())
+           return MethodInfo.nameClinit;
+       else
+           return declaringClass.getName();
+    }
+
+    /**
+     * Returns the class that declares this constructor.
+     */
+    public CtClass getDeclaringClass() {
+       return super.getDeclaringClass();
+    }
+
+    /**
+     * Obtains parameter types of this constructor.
+     */
+    public CtClass[] getParameterTypes() throws NotFoundException {
+       return super.getParameterTypes();
+    }
+
+    /**
+     * Returns the character string representing the parameter types.
+     * If two constructors have the same parameter types,
+     * <code>getSignature()</code> returns the same string.
+     */
+    public String getSignature() {
+       return super.getSignature();
+    }
+
+    /**
+     * Obtains exceptions that this constructor may throw.
+     */
+    public CtClass[] getExceptionTypes() throws NotFoundException {
+       return super.getExceptionTypes();
+    }
+
+    /**
+     * Sets exceptions that this constructor may throw.
+     */
+    public void setExceptionTypes(CtClass[] types)
+       throws NotFoundException
+    {
+       super.setExceptionTypes(types);
+    }
+
+    /**
+     * Sets a constructor body.
+     *
+     * @param src      the source code representing the constructor body.
+     *                 It must be a single statement or block.
+     */
+    public void setBody(String src) throws CannotCompileException {
+       super.setBody(src);
+    }
+
+    /**
+     * Copies a constructor body from another constructor.
+     *
+     * <p>All occurrences of the class names in the copied body
+     * are replaced with the names specified by
+     * <code>map</code> if <code>map</code> is not <code>null</code>.
+     *
+     * @param src      the method that the body is copied from.
+     * @param map      the hashtable associating original class names
+     *                 with substituted names.
+     *                 It can be <code>null</code>.
+     */
+    public void setBody(CtConstructor src, ClassMap map)
+       throws CannotCompileException
+    {
+       setBody0(src.declaringClass, src.methodInfo,
+                declaringClass, methodInfo, map);
+    }
+
+    /**
+     * Obtains an attribute with the given name.
+     * If that attribute is not found in the class file, this
+     * method returns null.
+     *
+     * @param name             attribute name
+     */
+    public byte[] getAttribute(String name) {
+       return super.getAttribute(name);
+    }
+
+    /**
+     * Adds an attribute. The attribute is saved in the class file.
+     *
+     * @param name     attribute name
+     * @param data     attribute value
+     */
+    public void setAttribute(String name, byte[] data) {
+       super.setAttribute(name, data);
+    }
+
+    /**
+     * Declares to use <code>$cflow</code> for this constructor.
+     * If <code>$cflow</code> is used, the class files modified
+     * with Javassist requires a support class
+     * <code>javassist.runtime.Cflow</code> at runtime
+     * (other Javassist classes are not required at runtime).
+     *
+     * <p>Every <code>$cflow</code> variable is given a unique name.
+     * For example, if the given name is <code>"Point.paint"</code>,
+     * then the variable is indicated by <code>$cflow(Point.paint)</code>.
+     *
+     * @param name     <code>$cflow</code> name.  It can include
+     *                 alphabets, numbers, <code>_</code>,
+     *                 <code>$</code>, and <code>.</code> (dot).
+     *
+     * @see javassist.runtime.Cflow
+     */
+    public void useCflow(String name) throws CannotCompileException {
+       super.useCflow(name);
+    }
+
+    /**
+     * Modifies the constructor body.
+     *
+     * @param converter                specifies how to modify.
+     */
+    public void instrument(CodeConverter converter)
+       throws CannotCompileException
+    {
+       super.instrument(converter);
+    }
+
+    /**
+     * Modifies the constructor body.
+     *
+     * @param editor           specifies how to modify.
+     */
+    public void instrument(ExprEditor editor)
+       throws CannotCompileException
+    {
+       super.instrument(editor);
+    }
+
+    /**
+     * Inserts bytecode at the beginning of the constructor body.
+     * Since the bytecode is inserted before a constructor in the super
+     * class or this class is called, the bytecode is subject
+     * to constraints described
+     * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed).
+     * For example, it cannot access instance members although it can access
+     * static members.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     */
+    public void insertBefore(String src) throws CannotCompileException {
+       super.insertBefore(src);
+    }
+
+    /**
+     * Inserts bytecode just after another constructor in the super class
+     * or this class is called.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     */
+    public void insertBeforeBody(String src) throws CannotCompileException {
+       declaringClass.checkModify();
+       CodeAttribute ca = methodInfo.getCodeAttribute();
+       CodeIterator iterator = ca.iterator();
+       Bytecode b = new Bytecode(methodInfo.getConstPool(),
+                                 ca.getMaxStack(), ca.getMaxLocals());
+       b.setStackDepth(ca.getMaxStack());
+       Javac jv = new Javac(b, declaringClass);
+       try {
+           jv.recordParams(getParameterTypes(), false);
+           jv.compileStmnt(src);
+           ca.setMaxStack(b.getMaxStack());
+           ca.setMaxLocals(b.getMaxLocals());
+           iterator.skipConstructor();
+           int pos = iterator.insertEx(b.get());
+           iterator.insert(b.getExceptionTable(), pos);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+       catch (BadBytecode e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    /**
+     * Inserts bytecode at the end of the constructor body.
+     * The bytecode is inserted just before every return insturction.
+     * It is not executed when an exception is thrown.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     */
+    public void insertAfter(String src)
+       throws CannotCompileException
+    {
+       super.insertAfter(src);
+    }
+
+    /**
+     * Inserts bytecode at the end of the constructor body.
+     * The bytecode is inserted just before every return insturction.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     * @param asFinally                true if the inserted bytecode is executed
+     *                 not only when the transfer normally returns
+     *                 but also when an exception is thrown.
+     */
+    public void insertAfter(String src, boolean asFinally)
+       throws CannotCompileException
+    {
+       super.insertAfter(src, asFinally);
+    }
+
+    /**
+     * Adds a catch clause that handles an exception thrown in the
+     * constructor body.
+     * The catch clause must end with a return or throw statement.
+     *
+     * @param src      the source code representing the catch clause.
+     *                 It must be a single statement or block.
+     * @param exceptionType    the type of the exception handled by the
+     *                         catch clause.
+     * @param exceptionName    the name of the variable containing the
+     *                         caught exception.
+     */
+    public void addCatch(String src, CtClass exceptionType,
+                        String exceptionName)
+       throws CannotCompileException
+    {
+       super.addCatch(src, exceptionType, exceptionName);
+    }
+}
diff --git a/src/main/javassist/CtField.java b/src/main/javassist/CtField.java
new file mode 100644 (file)
index 0000000..6bb45e9
--- /dev/null
@@ -0,0 +1,1107 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.compiler.Javac;
+import javassist.compiler.CompileError;
+import javassist.compiler.ast.ASTree;
+
+/**
+ * An instance of CtField represents a field.
+ *
+ * @see CtClass#getDeclaredFields()
+ */
+public class CtField extends CtMember {
+    protected FieldInfo fieldInfo;
+    CtField next;
+
+    /**
+     * Creates a <code>CtField</code> object.
+     * The created field must be added to a class
+     * with <code>CtClass.addField()</code>.
+     * An initial value of the field is specified
+     * by a <code>CtField.Initializer</code> object.
+     *
+     * <p>If getter and setter methods are needed,
+     * call <code>CtNewMethod.getter()</code> and 
+     * <code>CtNewMethod.setter()</code>.
+     *
+     * @param type             field type
+     * @param name             field name
+     * @param declaring                the class to which the field will be added.
+     *
+     * @see CtClass#addField(CtField)
+     * @see CtNewMethod#getter(String,CtField)
+     * @see CtNewMethod#setter(String,CtField)
+     * @see CtField.Initializer
+     */
+    public CtField(CtClass type, String name, CtClass declaring)
+       throws CannotCompileException
+    {
+       this(Descriptor.of(type), name, declaring);
+    }
+
+    /**
+     * Creates a copy of the given field.
+     * The created field must be added to a class
+     * with <code>CtClass.addField()</code>.
+     * An initial value of the field is specified
+     * by a <code>CtField.Initializer</code> object.
+     *
+     * <p>If getter and setter methods are needed,
+     * call <code>CtNewMethod.getter()</code> and 
+     * <code>CtNewMethod.setter()</code>.
+     *
+     * @param src              the original field
+     * @param declaring                the class to which the field will be added.
+     * @see CtNewMethod#getter(String,CtField)
+     * @see CtNewMethod#setter(String,CtField)
+     * @see CtField.Initializer
+     */
+    public CtField(CtField src, CtClass declaring)
+       throws CannotCompileException
+    {
+       this(src.fieldInfo.getDescriptor(), src.fieldInfo.getName(),
+            declaring);
+    }
+
+    private CtField(String typeDesc, String name, CtClass clazz)
+       throws CannotCompileException
+    {
+       super(clazz);
+       next = null;
+       ClassFile cf = clazz.getClassFile2();
+       if (cf == null)
+           throw new CannotCompileException("bad declaring class: "
+                                            + clazz.getName());
+
+       fieldInfo = new FieldInfo(cf.getConstPool(), name, typeDesc);
+    }
+
+    CtField(FieldInfo fi, CtClass clazz) {
+       super(clazz);
+       fieldInfo = fi;
+       next = null;
+    }
+
+    /* Javac.CtFieldWithInit overrides.
+     */
+    protected ASTree getInitAST() { return null; }
+
+    /* Called by CtClassType.addField().
+     */
+    Initializer getInit() {
+       ASTree tree = getInitAST();
+       if (tree == null)
+           return null;
+       else
+           return Initializer.byExpr(tree);
+    }
+
+    /**
+     * Compiles the given source code and creates a field.
+     * Examples of the source code are:
+     *
+     * <ul><pre>
+     * "public String name;"
+     * "public int k = 3;"</pre></ul>
+     *
+     * <p>Note that the source code ends with <code>';'</code>
+     * (semicolon).
+     *
+     * @param src              the source text.
+     * @param declaring    the class to which the created field is added.
+     */
+    public static CtField make(String src, CtClass declaring)
+       throws CannotCompileException
+    {
+       Javac compiler = new Javac(declaring);
+       try {
+           CtMember obj = compiler.compile(src);
+           if (obj instanceof CtField)
+               return (CtField)obj; // an instance of Javac.CtFieldWithInit
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+
+       throw new CannotCompileException("not a field");
+    }
+
+    static CtField append(CtField list, CtField tail) {
+       tail.next = null;
+       if (list == null)
+           return tail;
+       else {
+           CtField lst = list;
+           while (lst.next != null)
+               lst = lst.next;
+
+           lst.next = tail;
+           return list;
+       }
+    }
+
+    static int count(CtField f) {
+       int n = 0;
+       while (f != null) {
+           ++n;
+           f = f.next;
+       }
+
+       return n;
+    }
+
+    /**
+     * Returns the FieldInfo representing the field in the class file.
+     */
+    public FieldInfo getFieldInfo() {
+       declaringClass.checkModify();
+       return fieldInfo;
+    }
+
+    /**
+     * Undocumented method.  Do not use; internal-use only.
+     */
+    public FieldInfo getFieldInfo2() { return fieldInfo; }
+
+    /**
+     * Returns the class declaring the field.
+     */
+    public CtClass getDeclaringClass() {
+       // this is redundant but for javadoc.
+       return super.getDeclaringClass();
+    }
+
+    /**
+     * Returns the name of the field.
+     */
+    public String getName() {
+       return fieldInfo.getName();
+    }
+
+    /**
+     * Changes the name of the field.
+     */
+    public void setName(String newName) {
+       fieldInfo.setName(newName);
+    }
+
+    /**
+     * Returns the encoded modifiers of the field.
+     *
+     * @see Modifier
+     */
+    public int getModifiers() {
+       return AccessFlag.toModifier(fieldInfo.getAccessFlags());
+    }
+
+    /**
+     * Sets the encoded modifiers of the field.
+     *
+     * @see Modifier
+     */
+    public void setModifiers(int mod) {
+       fieldInfo.setAccessFlags(AccessFlag.of(mod));
+    }
+
+    /**
+     * Returns the type of the field.
+     */
+    public CtClass getType() throws NotFoundException {
+       return Descriptor.toCtClass(fieldInfo.getDescriptor(),
+                                   declaringClass.getClassPool());
+    }
+
+    /**
+     * Sets the type of the field.
+     */
+    public void setType(CtClass clazz) {
+       declaringClass.checkModify();
+       fieldInfo.setDescriptor(Descriptor.of(clazz));
+    }
+
+    /**
+     * Obtains an attribute with the given name.
+     * If that attribute is not found in the class file, this
+     * method returns null.
+     *
+     * @param name             attribute name
+     */
+    public byte[] getAttribute(String name) {
+       AttributeInfo ai = fieldInfo.getAttribute(name);
+       if (ai == null)
+           return null;
+       else
+           return ai.get();
+    }
+
+    /**
+     * Adds an attribute. The attribute is saved in the class file.
+     *
+     * @param name     attribute name
+     * @param data     attribute value
+     */
+    public void setAttribute(String name, byte[] data) {
+       declaringClass.checkModify();
+       fieldInfo.addAttribute(new AttributeInfo(fieldInfo.getConstPool(),
+                                                name, data));
+    }
+
+    // inner classes
+
+    /**
+     * Instances of this class specify how to initialize a field.
+     * <code>Initializer</code> is passed to
+     * <code>CtClass.addField()</code> with a <code>CtField</code>.
+     *
+     * <p>This class cannot be instantiated with the <code>new</code> operator.
+     * Factory methods such as <code>byParameter()</code> and
+     * <code>byNew</code>
+     * must be used for the instantiation.  They create a new instance with
+     * the given parameters and return it.
+     *
+     * @see CtClass#addField(CtField,CtField.Initializer)
+     */
+    public static abstract class Initializer {
+       /**
+        * Makes an initializer that assigns a constant integer value.
+        * The field must be integer type.
+        */
+       public static Initializer constant(int i) {
+           return new IntInitializer(i);
+       }
+
+       /**
+        * Makes an initializer that assigns a constant long value.
+        * The field must be long type.
+        */
+       public static Initializer constant(long l) {
+           return new LongInitializer(l);
+       }
+
+       /**
+        * Makes an initializer that assigns a constant double value.
+        * The field must be double type.
+        */
+       public static Initializer constant(double d) {
+           return new DoubleInitializer(d);
+       }
+
+       /**
+        * Makes an initializer that assigns a constant string value.
+        * The field must be <code>java.lang.String</code> type.
+        */
+       public static Initializer constant(String s) {
+           return new StringInitializer(s);
+       }
+
+       /**
+        * Makes an initializer using a constructor parameter.
+        *
+        * <p>The initial value is the
+        * N-th parameter given to the constructor of the object including
+        * the field.  If the constructor takes less than N parameters,
+        * the field is not initialized.
+        * If the field is static, it is never initialized.
+        *
+        * @param nth           the n-th (&gt;= 0) parameter is used as
+        *                      the initial value.
+        *                      If nth is 0, then the first parameter is
+        *                      used.
+        */
+       public static Initializer byParameter(int nth) {
+           ParamInitializer i = new ParamInitializer();
+           i.nthParam = nth;
+           return i;
+       }
+
+       /**
+        * Makes an initializer creating a new object.
+        *
+        * <p>This initializer creates a new object and uses it as the initial
+        * value of the field.  The constructor of the created object receives
+        * the parameter:
+        *
+        * <ul><code>Object obj</code> - the object including the field.<br>
+        * </ul>
+        *
+        * <p>If the initialized field is static, then the constructor does
+        * not receive any parameters.
+        *
+        * @param objectType    the class instantiated for the initial value.
+        */
+       public static Initializer byNew(CtClass objectType) {
+           NewInitializer i = new NewInitializer();
+           i.objectType = objectType;
+           i.stringParams = null;
+           i.withConstructorParams = false;
+           return i;
+       }
+
+       /**
+        * Makes an initializer creating a new object.
+        *
+        * <p>This initializer creates a new object and uses it as the initial
+        * value of the field.  The constructor of the created object receives
+        * the parameters:
+        *
+        * <ul><code>Object obj</code> - the object including the field.<br>
+        *     <code>String[] strs</code> - the character strings specified
+        *                              by <code>stringParams</code><br>
+        * </ul>
+        *
+        * <p>If the initialized field is static, then the constructor
+        * receives only <code>strs</code>.
+        *
+        * @param objectType    the class instantiated for the initial value.
+        * @param stringParams  the array of strings passed to the
+        *                      constructor.
+        */
+       public static Initializer byNew(CtClass objectType,
+                                            String[] stringParams) {
+           NewInitializer i = new NewInitializer();
+           i.objectType = objectType;
+           i.stringParams = stringParams;
+           i.withConstructorParams = false;
+           return i;
+       }
+
+       /**
+        * Makes an initializer creating a new object.
+        *
+        * <p>This initializer creates a new object and uses it as the initial
+        * value of the field.  The constructor of the created object receives
+        * the parameters:
+        *
+        * <ul><code>Object obj</code> - the object including the field.<br>
+        *     <code>Object[] args</code> - the parameters passed to the
+        *                      constructor of the object including the
+        *                      filed.
+        * </ul>
+        *
+        * <p>If the initialized field is static, then the constructor does
+        * not receive any parameters.
+        *
+        * @param objectType    the class instantiated for the initial value.
+        *
+        * @see javassist.CtField.Initializer#byNewArray(CtClass,int)
+        * @see javassist.CtField.Initializer#byNewArray(CtClass,int[])
+        */
+       public static Initializer byNewWithParams(CtClass objectType) {
+           NewInitializer i = new NewInitializer();
+           i.objectType = objectType;
+           i.stringParams = null;
+           i.withConstructorParams = true;
+           return i;
+       }
+
+       /**
+        * Makes an initializer creating a new object.
+        *
+        * <p>This initializer creates a new object and uses it as the initial
+        * value of the field.  The constructor of the created object receives
+        * the parameters:
+        *
+        * <ul><code>Object obj</code> - the object including the field.<br>
+        *     <code>String[] strs</code> - the character strings specified
+        *                              by <code>stringParams</code><br>
+        *     <code>Object[] args</code> - the parameters passed to the
+        *                      constructor of the object including the
+        *                      filed.
+        * </ul>
+        *
+        * <p>If the initialized field is static, then the constructor receives
+        * only <code>strs</code>.
+        *
+        * @param objectType    the class instantiated for the initial value.
+        * @param stringParams  the array of strings passed to the
+        *                              constructor.
+        */
+       public static Initializer byNewWithParams(CtClass objectType,
+                                              String[] stringParams) {
+           NewInitializer i = new NewInitializer();
+           i.objectType = objectType;
+           i.stringParams = stringParams;
+           i.withConstructorParams = true;
+           return i;
+       }
+
+       /**
+        * Makes an initializer calling a static method.
+        *
+        * <p>This initializer calls a static method and uses the returned
+        * value as the initial value of the field.
+        * The called method receives the parameters:
+        *
+        * <ul><code>Object obj</code> - the object including the field.<br>
+        * </ul>
+        *
+        * <p>If the initialized field is static, then the method does
+        * not receive any parameters.
+        *
+        * <p>The type of the returned value must be the same as the field
+        * type.
+        *
+        * @param methodClass   the class that the static method is
+        *                              declared in.
+        * @param methodName    the name of the satic method.
+        */
+       public static Initializer byCall(CtClass methodClass,
+                                             String methodName) {
+           MethodInitializer i = new MethodInitializer();
+           i.objectType = methodClass;
+           i.methodName = methodName;
+           i.stringParams = null;
+           i.withConstructorParams = false;
+           return i;
+       }
+
+       /**
+        * Makes an initializer calling a static method.
+        *
+        * <p>This initializer calls a static method and uses the returned
+        * value as the initial value of the field.  The called method
+        * receives the parameters:
+        *
+        * <ul><code>Object obj</code> - the object including the field.<br>
+        *     <code>String[] strs</code> - the character strings specified
+        *                              by <code>stringParams</code><br>
+        * </ul>
+        *
+        * <p>If the initialized field is static, then the method
+        * receive only <code>strs</code>.
+        *
+        * <p>The type of the returned value must be the same as the field
+        * type.
+        *
+        * @param methodClass   the class that the static method is
+        *                              declared in.
+        * @param methodName    the name of the satic method.
+        * @param stringParams  the array of strings passed to the
+        *                              static method.
+        */
+       public static Initializer byCall(CtClass methodClass,
+                                             String methodName,
+                                             String[] stringParams) {
+           MethodInitializer i = new MethodInitializer();
+           i.objectType = methodClass;
+           i.methodName = methodName;
+           i.stringParams = stringParams;
+           i.withConstructorParams = false;
+           return i;
+       }
+
+       /**
+        * Makes an initializer calling a static method.
+        *
+        * <p>This initializer calls a static method and uses the returned
+        * value as the initial value of the field.  The called method
+        * receives the parameters:
+        *
+        * <ul><code>Object obj</code> - the object including the field.<br>
+        *     <code>Object[] args</code> - the parameters passed to the
+        *                      constructor of the object including the
+        *                      filed.
+        * </ul>
+        *
+        * <p>If the initialized field is static, then the method does
+        * not receive any parameters.
+        *
+        * <p>The type of the returned value must be the same as the field
+        * type.
+        *
+        * @param methodClass   the class that the static method is
+        *                              declared in.
+        * @param methodName    the name of the satic method.
+        */
+       public static Initializer byCallWithParams(CtClass methodClass,
+                                                       String methodName) {
+           MethodInitializer i = new MethodInitializer();
+           i.objectType = methodClass;
+           i.methodName = methodName;
+           i.stringParams = null;
+           i.withConstructorParams = true;
+           return i;
+       }
+
+       /**
+        * Makes an initializer calling a static method.
+        *
+        * <p>This initializer calls a static method and uses the returned
+        * value as the initial value of the field.  The called method
+        * receives the parameters:
+        *
+        * <ul><code>Object obj</code> - the object including the field.<br>
+        *     <code>String[] strs</code> - the character strings specified
+        *                              by <code>stringParams</code><br>
+        *     <code>Object[] args</code> - the parameters passed to the
+        *                      constructor of the object including the
+        *                      filed.
+        * </ul>
+        *
+        * <p>If the initialized field is static, then the method
+        * receive only <code>strs</code>.
+        *
+        * <p>The type of the returned value must be the same as the field
+        * type.
+        *
+        * @param methodClass   the class that the static method is
+        *                              declared in.
+        * @param methodName    the name of the satic method.
+        * @param stringParams  the array of strings passed to the
+        *                              static method.
+        */
+       public static Initializer byCallWithParams(CtClass methodClass,
+                               String methodName, String[] stringParams) {
+           MethodInitializer i = new MethodInitializer();
+           i.objectType = methodClass;
+           i.methodName = methodName;
+           i.stringParams = stringParams;
+           i.withConstructorParams = true;
+           return i;
+       }
+
+       /**
+        * Makes an initializer creating a new array.
+        *
+        * @param type  the type of the array.
+        * @param size  the size of the array.
+        * @throws NotFoundException    if the type of the array components
+        *                              is not found.
+        */
+       public static Initializer byNewArray(CtClass type, int size) 
+           throws NotFoundException
+       {
+           return new ArrayInitializer(type.getComponentType(), size);
+       }
+
+       /**
+        * Makes an initializer creating a new multi-dimensional array.
+        *
+        * @param type  the type of the array.
+        * @param sizes an <code>int</code> array of the size in every
+        *                      dimension.
+        *                      The first element is the size in the first
+        *                      dimension.  The second is in the second, etc.
+        */
+       public static Initializer byNewArray(CtClass type, int[] sizes) {
+           return new MultiArrayInitializer(type, sizes);
+       }
+
+       /**
+        * Makes an initializer.
+        *
+        * @param source        initializer expression.
+        */
+       public static Initializer byExpr(String source) {
+           return new CodeInitializer(source);
+       }
+
+       static Initializer byExpr(ASTree source) {
+           return new PtreeInitializer(source);
+       }
+
+       // Check whether this initializer is valid for the field type.
+       // If it is invaild, this method throws an exception.
+       void check(CtClass type) throws CannotCompileException {}
+
+       // produce codes for initialization
+       abstract int compile(CtClass type, String name, Bytecode code,
+                            CtClass[] parameters, Javac drv)
+           throws CannotCompileException;
+
+       // produce codes for initialization
+       abstract int compileIfStatic(CtClass type, String name,
+               Bytecode code, Javac drv) throws CannotCompileException;
+    }
+
+    static abstract class CodeInitializer0 extends Initializer {
+       abstract void compileExpr(Javac drv) throws CompileError;
+
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           try {
+               code.addAload(0);
+               compileExpr(drv);
+               code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
+               return code.getMaxStack();
+           }
+           catch (CompileError e) {
+               throw new CannotCompileException(e);
+           }
+       }
+
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           try {
+               compileExpr(drv);
+               code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type));
+               return code.getMaxStack();
+           }
+           catch (CompileError e) {
+               throw new CannotCompileException(e);
+           }
+       }
+    }
+
+    static class CodeInitializer extends CodeInitializer0 {
+       private String expression;
+
+       CodeInitializer(String expr) { expression = expr; }
+
+       void compileExpr(Javac drv) throws CompileError {
+           drv.compileExpr(expression);
+       }
+    }
+
+    static class PtreeInitializer extends CodeInitializer0 {
+       private ASTree expression;
+
+       PtreeInitializer(ASTree expr) { expression = expr; }
+
+       void compileExpr(Javac drv) throws CompileError {
+           drv.compileExpr(expression);
+       }
+    }
+
+    /**
+     * A field initialized with a parameter passed to the constructor
+     * of the class containing that field.
+     */
+    static class ParamInitializer extends Initializer {
+       int nthParam;
+
+       ParamInitializer() {}
+
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           if (parameters != null && nthParam < parameters.length) {
+               code.addAload(0);
+               int nth = nthParamToLocal(nthParam, parameters, false);
+               int s = code.addLoad(nth, type) + 1;
+               code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
+               return s;       // stack size
+           }
+           else
+               return 0;       // do not initialize
+       }
+
+       /**
+        * Computes the index of the local variable that the n-th parameter
+        * is assigned to.
+        *
+        * @param nth           n-th parameter
+        * @param params                list of parameter types
+        * @param isStatic              true if the method is static.
+        */
+       static int nthParamToLocal(int nth, CtClass[] params,
+                                  boolean isStatic) {
+           CtClass longType = CtClass.longType;
+           CtClass doubleType = CtClass.doubleType;
+           int k;
+           if (isStatic)
+               k = 0;
+           else
+               k = 1;  // 0 is THIS.
+
+           for (int i = 0; i < nth; ++i) {
+               CtClass type = params[i];
+               if (type == longType || type == doubleType)
+                   k += 2;
+               else
+                   ++k;
+           }
+
+           return k;
+       }
+
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           return 0;
+       }
+    }
+
+    /**
+     * A field initialized with an object created by the new operator.
+     */
+    static class NewInitializer extends Initializer {
+       CtClass objectType;
+       String[] stringParams;
+       boolean withConstructorParams;
+
+       NewInitializer() {}
+
+       /**
+        * Produces codes in which a new object is created and assigned to
+        * the field as the initial value.
+        */
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           int stacksize;
+
+           code.addAload(0);
+           code.addNew(objectType);
+           code.add(Bytecode.DUP);
+           code.addAload(0);
+
+           if (stringParams == null)
+               stacksize = 4;
+           else
+               stacksize = compileStringParameter(code) + 4;
+
+           if (withConstructorParams)
+               stacksize += CtNewWrappedMethod.compileParameterList(code,
+                                                           parameters, 1);
+
+           code.addInvokespecial(objectType, "<init>", getDescriptor());
+           code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
+           return stacksize;
+       }
+
+       private String getDescriptor() {
+           final String desc3
+       = "(Ljava/lang/Object;[Ljava/lang/String;[Ljava/lang/Object;)V";
+
+           if (stringParams == null)
+               if (withConstructorParams)
+                   return "(Ljava/lang/Object;[Ljava/lang/Object;)V";
+               else
+                   return "(Ljava/lang/Object;)V";
+           else
+               if (withConstructorParams)
+                   return desc3;
+               else
+                   return "(Ljava/lang/Object;[Ljava/lang/String;)V";
+       }
+
+       /**
+        * Produces codes for a static field.
+        */
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           String desc;
+
+           code.addNew(objectType);
+           code.add(Bytecode.DUP);
+
+           int stacksize = 2;
+           if (stringParams == null)
+               desc = "()V";
+           else {
+               desc = "([Ljava/lang/String;)V";
+               stacksize += compileStringParameter(code);
+           }
+
+           code.addInvokespecial(objectType, "<init>", desc);
+           code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type));
+           return stacksize;
+       }
+
+       protected final int compileStringParameter(Bytecode code)
+           throws CannotCompileException
+       {
+           int nparam = stringParams.length;
+           code.addIconst(nparam);
+           code.addAnewarray("java.lang.String");
+           for (int j = 0; j < nparam; ++j) {
+               code.add(Bytecode.DUP);         // dup
+               code.addIconst(j);                      // iconst_<j>
+               code.addLdc(stringParams[j]);   // ldc ...
+               code.add(Bytecode.AASTORE);             // aastore
+           }
+
+           return 4;
+       }
+
+    }
+
+    /**
+     * A field initialized with the result of a static method call.
+     */
+    static class MethodInitializer extends NewInitializer {
+       String methodName;
+       // the method class is specified by objectType.
+
+       MethodInitializer() {}
+
+       /**
+        * Produces codes in which a new object is created and assigned to
+        * the field as the initial value.
+        */
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           int stacksize;
+
+           code.addAload(0);
+           code.addAload(0);
+
+           if (stringParams == null)
+               stacksize = 2;
+           else
+               stacksize = compileStringParameter(code) + 2;
+
+           if (withConstructorParams)
+               stacksize += CtNewWrappedMethod.compileParameterList(code,
+                                                           parameters, 1);
+
+           String typeDesc = Descriptor.of(type);
+           String mDesc = getDescriptor() + typeDesc;
+           code.addInvokestatic(objectType, methodName, mDesc);
+           code.addPutfield(Bytecode.THIS, name, typeDesc);
+           return stacksize;
+       }
+
+       private String getDescriptor() {
+           final String desc3
+               = "(Ljava/lang/Object;[Ljava/lang/String;[Ljava/lang/Object;)";
+
+           if (stringParams == null)
+               if (withConstructorParams)
+                   return "(Ljava/lang/Object;[Ljava/lang/Object;)";
+               else
+                   return "(Ljava/lang/Object;)";
+           else
+               if (withConstructorParams)
+                   return desc3;
+               else
+                   return "(Ljava/lang/Object;[Ljava/lang/String;)";
+       }
+
+       /**
+        * Produces codes for a static field.
+        */
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           String desc;
+
+           int stacksize = 1;
+           if (stringParams == null)
+               desc = "()";
+           else {
+               desc = "([Ljava/lang/String;)";
+               stacksize += compileStringParameter(code);
+           }
+
+           String typeDesc = Descriptor.of(type);
+           code.addInvokestatic(objectType, methodName, desc + typeDesc);
+           code.addPutstatic(Bytecode.THIS, name, typeDesc);
+           return stacksize;
+       }
+    }
+
+    static class IntInitializer extends Initializer {
+       int value;
+
+       IntInitializer(int v) { value = v; }
+
+       void check(CtClass type) throws CannotCompileException {
+           if (type != CtClass.intType)
+               throw new CannotCompileException("type mismatch");
+       }
+
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           code.addAload(0);
+           code.addIconst(value);
+           code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
+           return 2;   // stack size
+       }
+
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           code.addIconst(value);
+           code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type));
+           return 1;   // stack size
+       }
+    }
+
+    static class LongInitializer extends Initializer {
+       long value;
+
+       LongInitializer(long v) { value = v; }
+
+       void check(CtClass type) throws CannotCompileException {
+           if (type != CtClass.longType)
+               throw new CannotCompileException("type mismatch");
+       }
+
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           code.addAload(0);
+           code.addLdc2w(value);
+           code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
+           return 3;   // stack size
+       }
+
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           code.addLdc2w(value);
+           code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type));
+           return 2;   // stack size
+       }
+    }
+
+    static class DoubleInitializer extends Initializer {
+       double value;
+
+       DoubleInitializer(double v) { value = v; }
+
+       void check(CtClass type) throws CannotCompileException {
+           if (type != CtClass.doubleType)
+               throw new CannotCompileException("type mismatch");
+       }
+
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           code.addAload(0);
+           code.addLdc2w(value);
+           code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
+           return 3;   // stack size
+       }
+
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           code.addLdc2w(value);
+           code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type));
+           return 2;   // stack size
+       }
+    }
+
+    static class StringInitializer extends Initializer {
+       String value;
+
+       StringInitializer(String v) { value = v; }
+
+       void check(CtClass type) throws CannotCompileException {
+           if (!type.getName().equals("java.lang.String"))
+               throw new CannotCompileException("type mismatch");
+       }
+
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           code.addAload(0);
+           code.addLdc(value);
+           code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
+           return 2;   // stack size
+       }
+
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           code.addLdc(value);
+           code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type));
+           return 1;   // stack size
+       }
+    }
+
+    static class ArrayInitializer extends Initializer {
+       CtClass type;
+       int size;
+
+       ArrayInitializer(CtClass t, int s) { type = t; size = s; }
+
+       private void addNewarray(Bytecode code) {
+           if (type.isPrimitive())
+               code.addNewarray(((CtPrimitiveType)type).getArrayType(),
+                                size);
+           else
+               code.addAnewarray(type, size);
+       }
+
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           code.addAload(0);
+           addNewarray(code);
+           code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
+           return 2;   // stack size
+       }
+
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           addNewarray(code);
+           code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type));
+           return 1;   // stack size
+       }
+    }
+
+    static class MultiArrayInitializer extends Initializer {
+       CtClass type;
+       int[] dim;
+
+       MultiArrayInitializer(CtClass t, int[] d) { type = t; dim = d; }
+
+       void check(CtClass type) throws CannotCompileException {
+           if (!type.isArray())
+               throw new CannotCompileException("type mismatch");
+       }
+
+       int compile(CtClass type, String name, Bytecode code,
+                   CtClass[] parameters, Javac drv)
+           throws CannotCompileException
+       {
+           code.addAload(0);
+           int s = code.addMultiNewarray(type, dim);
+           code.addPutfield(Bytecode.THIS, name, Descriptor.of(type));
+           return s + 1;       // stack size
+       }
+
+       int compileIfStatic(CtClass type, String name, Bytecode code,
+                           Javac drv) throws CannotCompileException
+       {
+           int s = code.addMultiNewarray(type, dim);
+           code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type));
+           return s;   // stack size
+       }
+    }
+}
diff --git a/src/main/javassist/CtMember.java b/src/main/javassist/CtMember.java
new file mode 100644 (file)
index 0000000..8621a18
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+/**
+ * An instance of <code>CtMember</code> represents a field, a constructor,
+ * or a method.
+ */
+public abstract class CtMember {
+    protected CtClass declaringClass;
+
+    protected CtMember(CtClass clazz) { declaringClass = clazz; }
+
+    /**
+     * Returns the class that declares this member.
+     */
+    public CtClass getDeclaringClass() { return declaringClass; }
+
+    /**
+     * Obtains the modifiers of the member.
+     *
+     * @return         modifiers encoded with
+     *                 <code>javassist.Modifier</code>.
+     * @see Modifier
+     */
+    public abstract int getModifiers();
+
+    /**
+     * Sets the encoded modifiers of the member.
+     *
+     * @see Modifier
+     */
+    public abstract void setModifiers(int mod);
+
+    /**
+     * Obtains the name of the member.
+     */
+    public abstract String getName();
+
+    /**
+     * Obtains an attribute with the given name.
+     * If that attribute is not found in the class file, this
+     * method returns null.
+     *
+     * @param name             attribute name
+     */
+    public abstract byte[] getAttribute(String name);
+
+    /**
+     * Adds an attribute. The attribute is saved in the class file.
+     *
+     * @param name     attribute name
+     * @param data     attribute value
+     */
+    public abstract void setAttribute(String name, byte[] data);
+}
diff --git a/src/main/javassist/CtMethod.java b/src/main/javassist/CtMethod.java
new file mode 100644 (file)
index 0000000..dbe1931
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.expr.ExprEditor;
+
+/* Some methods do nothing except calling the super's method.
+ * They might seem redundant but they are necessary so that javadoc
+ * includes the description of those methods in the page of this class.
+ */
+
+/**
+ * An instance of <code>CtMethod</code> represents a method.
+ *
+ * @see CtClass#getDeclaredMethods()
+ * @see CtNewMethod
+ */
+public final class CtMethod extends CtBehavior {
+    protected CtMethod next;
+    protected int cachedHashCode;
+
+    CtMethod(MethodInfo minfo, CtClass declaring) {
+       super(declaring, minfo);
+       next = null;
+       cachedHashCode = 0;
+    }
+
+    /**
+     * Creates a public abstract method.  The created method must be
+     * added to a class with <code>CtClass.addMethod()</code>.
+     *
+     * @param declaring                the class to which the created method is added.
+     * @param returnType       the type of the returned value
+     * @param mname            the method name
+     * @param parameters       a list of the parameter types
+     *
+     * @see CtClass#addMethod(CtMethod)
+     */
+    public CtMethod(CtClass returnType, String mname,
+                   CtClass[] parameters, CtClass declaring) {
+       this(null, declaring);
+       ConstPool cp = declaring.getClassFile2().getConstPool();
+       String desc = Descriptor.ofMethod(returnType, parameters);
+       methodInfo = new MethodInfo(cp, mname, desc);
+       setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT);
+    }
+
+    /**
+     * Creates a copy of a <code>CtMethod</code> object.
+     * The created method must be
+     * added to a class with <code>CtClass.addMethod()</code>.
+     *
+     * <p>All occurrences of class names in the created method
+     * are replaced with names specified by
+     * <code>map</code> if <code>map</code> is not <code>null</code>.
+     *
+     * <p>For example, suppose that a method <code>at()</code> is as
+     * follows:
+     *
+     * <ul><pre>public X at(int i) {
+     *     return (X)super.elementAt(i);
+     * }</pre></ul>
+     *
+     * <p>(<code>X</code> is a class name.)  If <code>map</code> substitutes
+     * <code>String</code> for <code>X</code>, then the created method is:
+     *
+     * <ul><pre>public String at(int i) {
+     *     return (String)super.elementAt(i);
+     * }</pre></ul>
+     *
+     * <p>By default, all the occurrences of the names of the class
+     * declaring <code>at()</code> and the superclass are replaced
+     * with the name of the class and the superclass that the
+     * created method is added to.
+     * This is done whichever <code>map</code> is null or not.
+     * To prevent this replacement, call <code>ClassMap.fix()</code>.
+     *
+     * <p><b>Note:</b> if the <code>.class</code> notation (for example,
+     * <code>String.class</code>) is included in an expression, the
+     * Javac compiler may produce a helper method.
+     * Since this constructor never
+     * copies this helper method, the programmers have the responsiblity of
+     * copying it.  Otherwise, use <code>Class.forName()</code> in the
+     * expression.
+     *
+     * @param src      the source method.
+     * @param declaring    the class to which the created method is added.
+     * @param map      the hashtable associating original class names
+     *                 with substituted names.
+     *                 It can be <code>null</code>.
+     *
+     * @see CtClass#addMethod(CtMethod)
+     * @see ClassMap#fix(String)
+     */
+    public CtMethod(CtMethod src, CtClass declaring, ClassMap map)
+       throws CannotCompileException
+    {
+       this(null, declaring);
+       MethodInfo srcInfo = src.methodInfo;
+       CtClass srcClass = src.getDeclaringClass();
+       ConstPool cp = declaring.getClassFile2().getConstPool();
+       if (map == null)
+           map = new ClassMap();
+
+       map.put(srcClass.getName(), declaring.getName());
+       try {
+           CtClass srcSuper = srcClass.getSuperclass();
+           if (srcSuper != null) {
+               String srcSuperName = srcSuper.getName();
+               if (!srcSuperName.equals(CtClass.javaLangObject))
+                   map.put(srcSuperName,
+                           declaring.getSuperclass().getName());
+           }
+
+           methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+       catch (BadBytecode e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    static CtMethod append(CtMethod list, CtMethod tail) {
+       tail.next = null;
+       if (list == null)
+           return tail;
+       else {
+           CtMethod lst = list;
+           while (lst.next != null)
+               lst = lst.next;
+
+           lst.next = tail;
+           return list;
+       }
+    }
+
+    static int count(CtMethod m) {
+       int n = 0;
+       while (m != null) {
+           ++n;
+           m = m.next;
+       }
+
+       return n;
+    }
+
+    /**
+     * Returns a hash code value for the method.
+     * If two methods have the same name and signature, then
+     * the hash codes for the two methods are equal.
+     */
+    public int hashCode() {
+       /* This method is overridden in ExistingMethod for optimization.
+        */
+       if (cachedHashCode == 0) {
+           String signature
+               = methodInfo.getName() + ':' + methodInfo.getDescriptor();
+
+           // System.identityHashCode() returns 0 only for null.
+           cachedHashCode = System.identityHashCode(signature.intern());
+       }
+
+       return cachedHashCode;
+    }
+
+    /**
+     * Indicates whether <code>obj</code> has the same name and the
+     * same signature as this method.
+     */
+    public boolean equals(Object obj) {
+       return obj != null && obj instanceof CtMethod
+              && obj.hashCode() == hashCode();
+    }
+
+    /**
+     * Returns the MethodInfo representing the method in the class file.
+     */
+    public MethodInfo getMethodInfo() {
+       return super.getMethodInfo();
+    }
+
+    /**
+     * Obtains the modifiers of the method.
+     *
+     * @return         modifiers encoded with
+     *                 <code>javassist.Modifier</code>.
+     * @see Modifier
+     */
+    public int getModifiers() {
+       return super.getModifiers();
+    }
+
+    /**
+     * Sets the encoded modifiers of the method.
+     *
+     * @see Modifier
+     */
+    public void setModifiers(int mod) {
+       super.setModifiers(mod);
+    }
+
+    /**
+     * Obtains the name of this method.
+     */
+    public String getName() {
+       return methodInfo.getName();
+    }
+
+    /**
+     * Changes the name of this method.
+     */
+    public void setName(String newname) {
+       declaringClass.checkModify();
+       methodInfo.setName(newname);
+    }
+
+    /**
+     * Returns the class that declares this method.
+     */
+    public CtClass getDeclaringClass() {
+       return super.getDeclaringClass();
+    }
+
+    /**
+     * Obtains parameter types of this method.
+     */
+    public CtClass[] getParameterTypes() throws NotFoundException {
+       return super.getParameterTypes();
+    }
+
+    /**
+     * Obtains the type of the returned value.
+     */
+    public CtClass getReturnType() throws NotFoundException {
+       return getReturnType0();
+    }
+
+    /**
+     * Returns the character string representing the parameter types
+     * and the return type.  If two methods have the same parameter types
+     * and the return type, <code>getSignature()</code> returns the
+     * same string.
+     */
+    public String getSignature() {
+       return super.getSignature();
+    }
+
+    /**
+     * Obtains exceptions that this method may throw.
+     */
+    public CtClass[] getExceptionTypes() throws NotFoundException {
+       return super.getExceptionTypes();
+    }
+
+    /**
+     * Sets exceptions that this method may throw.
+     *
+     * @param types    exception types (or null)
+     */
+    public void setExceptionTypes(CtClass[] types) throws NotFoundException {
+       super.setExceptionTypes(types);
+    }
+
+    /**
+     * Sets a method body.
+     *
+     * @param src      the source code representing the method body.
+     *                 It must be a single statement or block.
+     */
+    public void setBody(String src) throws CannotCompileException {
+       super.setBody(src);
+    }
+
+    /**
+     * Copies a method body from another method.
+     * If this method is abstract, the abstract modifier is removed
+     * after the method body is copied.
+     *
+     * <p>All occurrences of the class names in the copied method body
+     * are replaced with the names specified by
+     * <code>map</code> if <code>map</code> is not <code>null</code>.
+     *
+     * @param src      the method that the body is copied from.
+     * @param map      the hashtable associating original class names
+     *                 with substituted names.
+     *                 It can be <code>null</code>.
+     */
+    public void setBody(CtMethod src, ClassMap map)
+       throws CannotCompileException
+    {
+       setBody0(src.declaringClass, src.methodInfo,
+                declaringClass, methodInfo, map);
+    }
+
+    /**
+     * Replace a method body with a new method body wrapping the
+     * given method.
+     *
+     * @param mbody            the wrapped method
+     * @param constParam       the constant parameter given to
+     *                         the wrapped method
+     *                         (maybe <code>null</code>).
+     *
+     * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass)
+     */
+    public void setWrappedBody(CtMethod mbody, ConstParameter constParam)
+       throws CannotCompileException
+    {
+       declaringClass.checkModify();
+
+       CtClass clazz = getDeclaringClass();
+       CtClass[] params;
+       CtClass retType;
+       try {
+           params = getParameterTypes();
+           retType = getReturnType();
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+
+       Bytecode code = CtNewWrappedMethod.makeBody(clazz,
+                                                   clazz.getClassFile2(),
+                                                   mbody,
+                                                   params, retType,
+                                                   constParam);
+       CodeAttribute cattr = code.toCodeAttribute();
+       methodInfo.setCodeAttribute(cattr);
+       methodInfo.setAccessFlags(methodInfo.getAccessFlags()
+                                 & ~AccessFlag.ABSTRACT);
+    }
+
+    /**
+     * Obtains an attribute with the given name.
+     * If that attribute is not found in the class file, this
+     * method returns null.
+     *
+     * @param name             attribute name
+     */
+    public byte[] getAttribute(String name) {
+       return super.getAttribute(name);
+    }
+
+    /**
+     * Adds an attribute. The attribute is saved in the class file.
+     *
+     * @param name     attribute name
+     * @param data     attribute value
+     */
+    public void setAttribute(String name, byte[] data) {
+       super.setAttribute(name, data);
+    }
+
+    /**
+     * Declares to use <code>$cflow</code> for this method.
+     * If <code>$cflow</code> is used, the class files modified
+     * with Javassist requires a support class
+     * <code>javassist.runtime.Cflow</code> at runtime
+     * (other Javassist classes are not required at runtime).
+     *
+     * <p>Every <code>$cflow</code> variable is given a unique name.
+     * For example, if the given name is <code>"Point.paint"</code>,
+     * then the variable is indicated by <code>$cflow(Point.paint)</code>.
+     *
+     * @param name     <code>$cflow</code> name.  It can include
+     *                 alphabets, numbers, <code>_</code>,
+     *                 <code>$</code>, and <code>.</code> (dot).
+     *
+     * @see javassist.runtime.Cflow
+     */
+    public void useCflow(String name) throws CannotCompileException {
+       super.useCflow(name);
+    }
+
+    /**
+     * Modifies the method body.
+     *
+     * @param converter                specifies how to modify.
+     */
+    public void instrument(CodeConverter converter)
+       throws CannotCompileException
+    {
+       super.instrument(converter);
+    }
+
+    /**
+     * Modifies the method body.
+     *
+     * @param editor           specifies how to modify.
+     */
+    public void instrument(ExprEditor editor)
+       throws CannotCompileException
+    {
+       super.instrument(editor);
+    }
+
+    /**
+     * Inserts bytecode at the beginning of the method body.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     */
+    public void insertBefore(String src) throws CannotCompileException {
+       super.insertBefore(src);
+    }
+
+    /**
+     * Inserts bytecode at the end of the method body.
+     * The bytecode is inserted just before every return insturction.
+     * It is not executed when an exception is thrown.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     */
+    public void insertAfter(String src)
+       throws CannotCompileException
+    {
+       super.insertAfter(src);
+    }
+
+    /**
+     * Inserts bytecode at the end of the method body.
+     * The bytecode is inserted just before every return insturction.
+     *
+     * @param src      the source code representing the inserted bytecode.
+     *                 It must be a single statement or block.
+     * @param asFinally                true if the inserted bytecode is executed
+     *                 not only when the transfer normally returns
+     *                 but also when an exception is thrown.
+     */
+    public void insertAfter(String src, boolean asFinally)
+       throws CannotCompileException
+    {
+       super.insertAfter(src, asFinally);
+    }
+
+    /**
+     * Adds a catch clause that handles an exception thrown in the
+     * method body.
+     * The catch clause must end with a return or throw statement.
+     *
+     * @param src      the source code representing the catch clause.
+     *                 It must be a single statement or block.
+     * @param exceptionType    the type of the exception handled by the
+     *                         catch clause.
+     * @param exceptionName    the name of the variable containing the
+     *                         caught exception.
+     */
+    public void addCatch(String src, CtClass exceptionType,
+                        String exceptionName)
+       throws CannotCompileException
+    {
+       super.addCatch(src, exceptionType, exceptionName);
+    }
+
+    // inner classes
+
+    /**
+     * Instances of this class represent a constant parameter.
+     * They are used to specify the parameter given to the methods
+     * created by <code>CtNewMethod.wrapped()</code>.
+     *
+     * @see CtMethod#setWrappedBody(CtMethod,CtMethod.ConstParameter)
+     * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass)
+     * @see CtNewConstructor#make(CtClass[],CtClass[],int,CtMethod,CtMethod.ConstParameter,CtClass)
+     */
+    public static class ConstParameter {
+       /**
+        * Makes an integer constant.
+        *
+        * @param i             the constant value.
+        */
+       public static ConstParameter integer(int i) {
+           return new IntConstParameter(i);
+       }
+
+       /**
+        * Makes an <code>String</code> constant.
+        *
+        * @param s             the constant value.
+        */
+       public static ConstParameter string(String s) {
+           return new StringConstParameter(s);
+       }
+
+       ConstParameter() {}
+
+       int compile(Bytecode code) throws CannotCompileException {
+           return 0;
+       }
+
+       String descriptor() {
+           return defaultDescriptor();
+       }
+
+       static String defaultDescriptor() {
+           return "([Ljava/lang/Object;)Ljava/lang/Object;";
+       }
+
+       String constDescriptor() {
+           return defaultConstDescriptor();
+       }
+
+       /**
+        * Returns the descriptor for constructors.
+        */
+       static String defaultConstDescriptor() {
+           return "([Ljava/lang/Object;)V";
+       }
+    }
+
+    static class IntConstParameter extends ConstParameter {
+       int param;
+
+       IntConstParameter(int i) {
+           param = i;
+       }
+
+       int compile(Bytecode code) throws CannotCompileException {
+           code.addIconst(param);
+           return 1;
+       }
+
+       String descriptor() {
+           return "([Ljava/lang/Object;I)Ljava/lang/Object;";
+       }
+
+       String constDescriptor() {
+           return "([Ljava/lang/Object;I)V";
+       }
+    }
+
+    static class StringConstParameter extends ConstParameter {
+       String param;
+
+       StringConstParameter(String s) {
+           param = s;
+       }
+
+       int compile(Bytecode code) throws CannotCompileException {
+           code.addLdc(param);
+           return 1;
+       }
+
+       String descriptor() {
+           return "([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
+       }
+
+       String constDescriptor() {
+           return "([Ljava/lang/Object;Ljava/lang/String;)V";
+       }
+    }
+}
diff --git a/src/main/javassist/CtNewClass.java b/src/main/javassist/CtNewClass.java
new file mode 100644 (file)
index 0000000..09bf345
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import javassist.bytecode.ClassFile;
+
+class CtNewClass extends CtClassType {
+    /* true if the class is an interface.
+     */
+    protected boolean hasConstructor;
+
+    CtNewClass(String name, ClassPool cp,
+              boolean isInterface, CtClass superclass) {
+       super(name, cp);
+       wasChanged = true;
+       eraseCache();
+       String superName;
+       if (superclass == null)
+           superName = null;
+       else
+           superName = superclass.getName();
+
+       classfile = new ClassFile(isInterface, name, superName);
+
+       setModifiers(Modifier.setPublic(getModifiers()));
+       hasConstructor = isInterface;
+    }
+
+    public void addConstructor(CtConstructor c)
+       throws CannotCompileException
+    {
+       hasConstructor = true;
+       super.addConstructor(c);
+    }
+
+    void toBytecode(DataOutputStream out)
+       throws CannotCompileException, IOException
+    {
+       if (!hasConstructor)
+           try {
+               inheritAllConstructors();
+           }
+           catch (NotFoundException e) {
+               throw new CannotCompileException(e);
+           }
+
+       super.toBytecode(out);
+    }
+
+    /**
+     * Adds constructors inhrited from the super class.
+     *
+     * <p>After this method is called, the class inherits all the
+     * constructors from the super class.  The added constructor
+     * calls the super's constructor with the same signature.
+     */
+    public void inheritAllConstructors()
+       throws CannotCompileException, NotFoundException
+    {
+       CtClass superclazz;
+       CtConstructor[] cs;
+
+       superclazz = getSuperclass();
+       cs = superclazz.getDeclaredConstructors();
+
+       int n = 0;
+       for (int i = 0; i < cs.length; ++i) {
+           CtConstructor c = cs[i];
+           if (Modifier.isPublic(c.getModifiers())) {
+               CtConstructor cons
+                   = CtNewConstructor.make(c.getParameterTypes(),
+                                           c.getExceptionTypes(), this);
+               addConstructor(cons);
+               ++n;
+           }
+       }
+
+       if (n < 1)
+           throw new CannotCompileException(
+                       "no public constructor in " + superclazz.getName());
+
+    }
+}
diff --git a/src/main/javassist/CtNewConstructor.java b/src/main/javassist/CtNewConstructor.java
new file mode 100644 (file)
index 0000000..13bae8a
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.compiler.Javac;
+import javassist.compiler.CompileError;
+import javassist.CtMethod.ConstParameter;
+
+/**
+ * A collection of static methods for creating a <code>CtConstructor</code>.
+ * An instance of this class does not make any sense.
+ *
+ * @see CtClass#addConstructor(CtConstructor)
+ */
+public class CtNewConstructor {
+    /**
+     * Specifies that no parameters are passed to a super-class'
+     * constructor.  That is, the default constructor is invoked.
+     */
+    public static final int PASS_NONE = 0;     // call super()
+
+    /**
+     * Specifies that parameters are converted into an array of
+     * <code>Object</code> and passed to a super-class'
+     * constructor.
+     */
+    public static final int PASS_ARRAY = 1;    // an array of parameters
+
+    /**
+     * Specifies that parameters are passed <i>as is</i>
+     * to a super-class' constructor.  The signature of that
+     * constructor must be the same as that of the created constructor.
+     */
+    public static final int PASS_PARAMS = 2;
+
+    /**
+     * Compiles the given source code and creates a constructor.
+     * The source code must include not only the constructor body
+     * but the whole declaration.
+     *
+     * @param src              the source text. 
+     * @param declaring    the class to which the created constructor is added.
+     */
+    public static CtConstructor make(String src, CtClass declaring)
+       throws CannotCompileException
+    {
+       Javac compiler = new Javac(declaring);
+       try {
+           CtMember obj = compiler.compile(src);
+           if (obj instanceof CtConstructor)
+               return (CtConstructor)obj;
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+
+       throw new CannotCompileException("not a constructor");
+    }
+
+    /**
+     * Creates a public constructor.
+     *
+     * @param returnType       the type of the returned value
+     * @param mname            the method name
+     * @param parameters       a list of the parameter types
+     * @param exceptions       a list of the exception types
+     * @param src              the source text of the method body.
+     *                 It must be a block surrounded by <code>{}</code>.
+     * @param declaring    the class to which the created method is added.
+     */
+    public static CtConstructor make(CtClass[] parameters,
+                                    CtClass[] exceptions,
+                                    String body, CtClass declaring)
+       throws CannotCompileException
+    {
+       try {
+           CtConstructor cc = new CtConstructor(parameters, declaring);
+           cc.setExceptionTypes(exceptions);
+           cc.setBody(body);
+           return cc;
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    /**
+     * Creats a copy of a constructor.
+     *
+     * @param c                the copied constructor.
+     * @param declaring    the class to which the created method is added.
+     * @param map      the hashtable associating original class names
+     *                 with substituted names.
+     *                 It can be <code>null</code>.
+     *
+     * @see CtConstructor#CtConstructor(CtConstructor,CtClass,ClassMap)
+     */
+    public static CtConstructor copy(CtConstructor c, CtClass declaring,
+                               ClassMap map) throws CannotCompileException {
+       return new CtConstructor(c, declaring, map);
+    }
+
+    /**
+     * Creates a default (public) constructor.
+     *
+     * <p>The created constructor takes no parameter.  It calls
+     * <code>super()</code>.
+     */
+    public static CtConstructor defaultConstructor(CtClass declaring)
+       throws CannotCompileException
+    {
+       CtConstructor cons = new CtConstructor((CtClass[])null, declaring);
+
+       ConstPool cp = declaring.getClassFile2().getConstPool();
+       Bytecode code = new Bytecode(cp, 1, 1);
+       code.addAload(0);
+       try {
+           code.addInvokespecial(declaring.getSuperclass(),
+                                 "<init>", "()V");
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+
+       code.add(Bytecode.RETURN);
+
+       cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
+       return cons;
+    }
+
+    /**
+     * Creates a public constructor that only calls a constructor
+     * in the super class.  The created constructor receives parameters
+     * specified by <code>parameters</code> but calls the super's
+     * constructor without those parameters (that is, it calls the default
+     * constructor).
+     *
+     * <p>The parameters passed to the created constructor should be
+     * used for field initialization.  <code>CtField.Initializer</code>
+     * objects implicitly insert initialization code in constructor
+     * bodies.
+     *
+     * @param parameters       parameter types
+     * @param exceptions       exception types
+     * @param declaring                the class to which the created constructor
+     *                         is added.
+     * @see CtField.Initializer#byParameter(int)
+     */
+    public static CtConstructor skeleton(CtClass[] parameters,
+                       CtClass[] exceptions, CtClass declaring)
+       throws CannotCompileException
+    {
+       return make(parameters, exceptions, PASS_NONE,
+                   null, null, declaring);
+    }
+
+    /**
+     * Creates a public constructor that only calls a constructor
+     * in the super class.  The created constructor receives parameters
+     * specified by <code>parameters</code> and calls the super's
+     * constructor with those parameters.
+     *
+     * @param parameters       parameter types
+     * @param exceptions       exception types
+     * @param declaring                the class to which the created constructor
+     *                         is added.
+     */
+    public static CtConstructor make(CtClass[] parameters,
+                                    CtClass[] exceptions, CtClass declaring)
+       throws CannotCompileException
+    {
+       return make(parameters, exceptions, PASS_PARAMS,
+                   null, null, declaring);
+    }
+
+    /**
+     * Creates a public constructor.
+     *
+     * <p>If <code>howto</code> is <code>PASS_PARAMS</code>,
+     * the created constructor calls the super's constructor with the
+     * same signature.  The superclass must contain
+     * a constructor taking the same set of parameters as the created one.
+     *
+     * <p>If <code>howto</code> is <code>PASS_NONE</code>,
+     * the created constructor calls the super's default constructor.
+     * The superclass must contain a constructor taking no parameters.
+     *
+     * <p>If <code>howto</code> is <code>PASS_ARRAY</code>,
+     * the created constructor calls the super's constructor
+     * with the given parameters in the form of an array of
+     * <code>Object</code>.  The signature of the super's constructor
+     * must be:
+     *
+     * <ul><code>constructor(Object[] params, &lt;type&gt; cvalue)
+     * </code></ul>
+     *
+     * <p>Here, <code>cvalue</code> is the constant value specified
+     * by <code>cparam</code>.
+     *
+     * <p>If <code>cparam</code> is <code>null</code>, the signature
+     * must be:
+     *
+     * <ul><code>constructor(Object[] params)</code></ul>
+     *
+     * <p>If <code>body</code> is not null, a copy of that method is
+     * embedded in the body of the created constructor.
+     * The embedded method is executed after
+     * the super's constructor is called and the values of fields are
+     * initialized.  Note that <code>body</code> must not
+     * be a constructor but a method.
+     *
+     * <p>Since the embedded method is wrapped
+     * in parameter-conversion code
+     * as in <code>CtNewMethod.wrapped()</code>,
+     * the constructor parameters are
+     * passed in the form of an array of <code>Object</code>.
+     * The method specified by <code>body</code> must have the
+     * signature shown below:
+     *
+     * <ul><code>Object method(Object[] params, &lt;type&gt; cvalue)
+     * </code></ul>
+     *
+     * <p>If <code>cparam</code> is <code>null</code>, the signature
+     * must be:
+     *
+     * <ul><code>Object method(Object[] params)</code></ul>
+     *
+     * <p>Although the type of the returned value is <code>Object</code>,
+     * the value must be always <code>null</code>.
+     *
+     * <p><i>Example:</i>
+     *
+     * <ul><pre>ClassPool pool = ... ;
+     * CtClass xclass = pool.makeClass("X");
+     * CtMethod method = pool.getMethod("Sample", "m");
+     * xclass.setSuperclass(pool.get("Y"));
+     * CtClass[] argTypes = { CtClass.intType };
+     * ConstParameter cparam = ConstParameter.string("test");
+     * CtConstructor c = CtNewConstructor.make(argTypes, null,
+     *                                  PASS_PARAMS, method, cparam, xclass);
+     * xclass.addConstructor(c);</pre></ul>
+     *
+     * <p>where the class <code>Sample</code> is as follows:
+     *
+     * <ul><pre>public class Sample {
+     *     public Object m(Object[] args, String msg) {
+     *         System.out.println(msg);
+     *         return null;
+     *     }
+     * }</pre></ul>
+     *
+     * <p>This program produces the following class:
+     *
+     * <ul><pre>public class X extends Y {
+     *     public X(int p0) {
+     *         super(p0);
+     *         String msg = "test";
+     *         Object[] args = new Object[] { p0 };
+     *         // begin of copied body
+     *         System.out.println(msg);
+     *         Object result = null;
+     *         // end
+     *     }
+     * }</pre></ul>
+     *
+     * @param parameters       a list of the parameter types
+     * @param exceptions       a list of the exceptions
+     * @param howto            how to pass parameters to the super-class'
+     *                         constructor (<code>PASS_NONE</code>,
+     *                         <code>PASS_ARRAY</code>,
+     *                         or <code>PASS_PARAMS</code>)
+     * @param body             appended body (may be <code>null</code>).
+     *                         It must be not a constructor but a method.
+     * @param cparam           constant parameter (may be <code>null</code>.)
+     * @param declaring                the class to which the created constructor
+     *                         is added.
+     *
+     * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass)
+     */
+    public static CtConstructor make(CtClass[] parameters,
+                                    CtClass[] exceptions, int howto,
+                                    CtMethod body, ConstParameter cparam,
+                                    CtClass declaring)
+       throws CannotCompileException
+    {
+       return CtNewWrappedConstructor.wrapped(parameters, exceptions,
+                                       howto, body, cparam, declaring);
+    }
+}
diff --git a/src/main/javassist/CtNewMethod.java b/src/main/javassist/CtNewMethod.java
new file mode 100644 (file)
index 0000000..0553290
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.compiler.Javac;
+import javassist.compiler.CompileError;
+import javassist.CtMethod.ConstParameter;
+
+/**
+ * A collection of static methods for creating a <code>CtMethod</code>.
+ * An instance of this class does not make any sense.
+ *
+ * @see CtClass#addMethod(CtMethod)
+ */
+public class CtNewMethod {
+
+    /**
+     * Compiles the given source code and creates a method.
+     * The source code must include not only the method body
+     * but the whole declaration, for example,
+     *
+     * <ul><pre>"public Object id(Object obj) { return obj; }"</pre></ul>
+     *
+     * @param src              the source text. 
+     * @param declaring    the class to which the created method is added.
+     */
+    public static CtMethod make(String src, CtClass declaring)
+       throws CannotCompileException
+    {
+       return make(src, declaring, null, null);
+    }
+
+    /**
+     * Compiles the given source code and creates a method.
+     * The source code must include not only the method body
+     * but the whole declaration, for example,
+     *
+     * <ul><pre>"public Object id(Object obj) { return obj; }"</pre></ul>
+     *
+     * <p>If the source code includes <code>$proceed()</code>, then
+     * it is compiled into a method call on the specified object.
+     *
+     * @param src              the source text. 
+     * @param declaring    the class to which the created method is added.
+     * @param delegateObj      the source text specifying the object
+     *                         that is called on by <code>$proceed()</code>.
+     * @param delegateMethod   the name of the method
+     *                         that is called by <code>$proceed()</code>.
+     */
+    public static CtMethod make(String src, CtClass declaring,
+                               String delegateObj, String delegateMethod)
+       throws CannotCompileException
+    {
+       Javac compiler = new Javac(declaring);
+       try {
+           if (delegateMethod != null)
+               compiler.recordProceed(delegateObj, delegateMethod);
+
+           CtMember obj = compiler.compile(src);
+           if (obj instanceof CtMethod)
+               return (CtMethod)obj;
+       }
+       catch (CompileError e) {
+           throw new CannotCompileException(e);
+       }
+
+       throw new CannotCompileException("not a method");
+    }
+
+    /**
+     * Creates a public method.
+     *
+     * @param returnType       the type of the returned value
+     * @param mname            the method name
+     * @param parameters       a list of the parameter types
+     * @param exceptions       a list of the exception types
+     * @param src              the source text of the method body.
+     *                 It must be a block surrounded by <code>{}</code>.
+     * @param declaring    the class to which the created method is added.
+     */
+    public static CtMethod make(CtClass returnType, String mname,
+                               CtClass[] parameters, CtClass[] exceptions,
+                               String body, CtClass declaring)
+       throws CannotCompileException
+    {
+       try {
+           CtMethod cm
+               = new CtMethod(returnType, mname, parameters, declaring);
+           cm.setExceptionTypes(exceptions);
+           cm.setBody(body);
+           return cm;
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    /**
+     * Creates a copy of a method.  This method is provided for creating
+     * a new method based on an existing method.
+     *
+     * @param src      the source method.
+     * @param declaring    the class to which the created method is added.
+     * @param map      the hashtable associating original class names
+     *                 with substituted names.
+     *                 It can be <code>null</code>.
+     *
+     * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap)
+     */
+    public static CtMethod copy(CtMethod src, CtClass declaring,
+                               ClassMap map) throws CannotCompileException {
+       return new CtMethod(src, declaring, map);
+    }
+
+    /**
+     * Creates a copy of a method with a new name.
+     * This method is provided for creating
+     * a new method based on an existing method.
+     *
+     * @param src      the source method.
+     * @param name     the name of the created method.
+     * @param declaring    the class to which the created method is added.
+     * @param map      the hashtable associating original class names
+     *                 with substituted names.
+     *                 It can be <code>null</code>.
+     *
+     * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap)
+     */
+    public static CtMethod copy(CtMethod src, String name, CtClass declaring,
+                               ClassMap map) throws CannotCompileException {
+       CtMethod cm = new CtMethod(src, declaring, map);
+       cm.setName(name);
+       return cm;
+    }
+
+    /**
+     * Creates a public abstract method.
+     *
+     * @param returnType       the type of the returned value
+     * @param mname            the method name
+     * @param parameters       a list of the parameter types
+     * @param exceptions       a list of the exception types
+     * @param declaring    the class to which the created method is added.
+     *
+     * @see CtMethod#CtMethod(CtClass,String,CtClass[],CtClass)
+     */
+    public static CtMethod abstractMethod(CtClass returnType,
+                                         String mname,
+                                         CtClass[] parameters,
+                                         CtClass[] exceptions,
+                                         CtClass declaring)
+       throws NotFoundException
+    {
+       CtMethod cm = new CtMethod(returnType, mname, parameters, declaring);
+       cm.setExceptionTypes(exceptions);
+       return cm;
+    }
+
+    /**
+     * Creates a public getter method.  The getter method returns the value
+     * of the specified field in the class to which this method is added.
+     * The created method is initially not static even if the field is
+     * static.  Change the modifiers if the method should be static.
+     *
+     * @param methodName       the name of the getter
+     * @param field            the field accessed.
+     */
+    public static CtMethod getter(String methodName, CtField field)
+       throws CannotCompileException
+    {
+       FieldInfo finfo = field.getFieldInfo2();
+       String fieldType = finfo.getDescriptor();
+       String desc = "()" + fieldType;
+       ConstPool cp = finfo.getConstPool();
+       MethodInfo minfo = new MethodInfo(cp, methodName, desc);
+       minfo.setAccessFlags(AccessFlag.PUBLIC);
+
+       Bytecode code = new Bytecode(cp, 2, 1);
+       try {
+           String fieldName = finfo.getName();
+           if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) {
+               code.addAload(0);
+               code.addGetfield(Bytecode.THIS, fieldName, fieldType);
+           }
+           else
+               code.addGetstatic(Bytecode.THIS, fieldName, fieldType);
+
+           code.addReturn(field.getType());
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+
+       minfo.setCodeAttribute(code.toCodeAttribute());
+       return new CtMethod(minfo, field.getDeclaringClass());
+    }
+
+    /**
+     * Creates a public setter method.  The setter method assigns the
+     * value of the first parameter to the specified field
+     * in the class to which this method is added.
+     * The created method is initially not static even if the field is
+     * static.  Change the modifiers if the method should be static.
+     *
+     * @param methodName       the name of the setter
+     * @param field            the field accessed.
+     */
+    public static CtMethod setter(String methodName, CtField field)
+       throws CannotCompileException
+    {
+       FieldInfo finfo = field.getFieldInfo2();
+       String fieldType = finfo.getDescriptor();
+       String desc = "(" + fieldType + ")V";
+       ConstPool cp = finfo.getConstPool();
+       MethodInfo minfo = new MethodInfo(cp, methodName, desc);
+       minfo.setAccessFlags(AccessFlag.PUBLIC);
+
+       Bytecode code = new Bytecode(cp, 3, 3);
+       try {
+           String fieldName = finfo.getName();
+           if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) {
+               code.addAload(0);
+               code.addLoad(1, field.getType());
+               code.addPutfield(Bytecode.THIS, fieldName, fieldType);
+           }
+           else {
+               code.addLoad(0, field.getType());
+               code.addPutstatic(Bytecode.THIS, fieldName, fieldType);
+           }
+
+           code.addReturn(null);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+
+       minfo.setCodeAttribute(code.toCodeAttribute());
+       return new CtMethod(minfo, field.getDeclaringClass());
+    }
+
+    /**
+     * Creates a method forwarding to a delegate in
+     * a super class.  The created method calls a method specified
+     * by <code>delegate</code> with all the parameters passed to the
+     * created method.  If the delegate method returns a value,
+     * the created method returns that value to the caller.
+     * The delegate method must be declared in a super class.
+     *
+     * <p>The following method is an example of the created method.
+     *
+     * <ul><pre>int f(int p, int q) {
+     *     return super.f(p, q);
+     * }</pre></ul>
+     *
+     * <p>The name of the created method can be changed by
+     * <code>setName()</code>.
+     *
+     * @param delegate   the method that the created method forwards to.
+     * @param declaring                the class to which the created method is
+     *                         added.
+     */
+    public static CtMethod delegator(CtMethod delegate, CtClass declaring)
+       throws CannotCompileException
+    {
+       try {
+           return delegator0(delegate, declaring);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    private static CtMethod delegator0(CtMethod delegate, CtClass declaring)
+       throws CannotCompileException, NotFoundException
+    {
+       MethodInfo deleInfo = delegate.getMethodInfo2();
+       String methodName = deleInfo.getName();
+       String desc = deleInfo.getDescriptor();
+       ConstPool cp = declaring.getClassFile2().getConstPool();
+       MethodInfo minfo = new MethodInfo(cp, methodName, desc);
+       minfo.setAccessFlags(deleInfo.getAccessFlags());
+
+       ExceptionsAttribute eattr = deleInfo.getExceptionsAttribute();
+       if (eattr != null)
+           minfo.setExceptionsAttribute(
+                               (ExceptionsAttribute)eattr.copy(cp, null));
+
+       Bytecode code = new Bytecode(cp, 0, 0);
+       boolean isStatic = Modifier.isStatic(delegate.getModifiers());
+       CtClass deleClass = delegate.getDeclaringClass();
+       CtClass[] params = delegate.getParameterTypes();
+       int s;
+       if (isStatic) {
+           s = code.addLoadParameters(params);
+           code.addInvokestatic(deleClass, methodName, desc);
+       }
+       else {
+           code.addLoad(0, deleClass);
+           s = code.addLoadParameters(params);
+           code.addInvokespecial(deleClass, methodName, desc);
+       }
+
+       code.addReturn(delegate.getReturnType());
+       code.setMaxLocals(++s);
+       code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value
+       minfo.setCodeAttribute(code.toCodeAttribute());
+       return new CtMethod(minfo, declaring);
+    }
+
+    /**
+     * Creates a wrapped method.  The wrapped method receives parameters
+     * in the form of an array of <code>Object</code>.
+     *
+     * <p>The body of the created method is a copy of the body of a method
+     * specified by <code>body</code>.  However, it is wrapped in
+     * parameter-conversion code.
+     *
+     * <p>The method specified by <code>body</code> must have this singature:
+     *
+     * <ul><code>Object method(Object[] params, &lt;type&gt; cvalue)
+     * </code></ul>
+     *
+     * <p>The type of the <code>cvalue</code> depends on
+     * <code>constParam</code>.
+     * If <code>constParam</code> is <code>null</code>, the signature
+     * must be:
+     *
+     * <ul><code>Object method(Object[] params)</code></ul>
+     *
+     * <p>The method body copied from <code>body</code> is wrapped in
+     * parameter-conversion code, which converts parameters specified by
+     * <code>parameterTypes</code> into an array of <code>Object</code>.
+     * The returned value is also converted from the <code>Object</code>
+     * type to the type specified by <code>returnType</code>.  Thus,
+     * the resulting method body is as follows:
+     *
+     * <ul><pre>Object[] params = new Object[] { p0, p1, ... };
+     * &lt;<i>type</i>&gt; cvalue = &lt;<i>constant-value</i>&gt;;
+     *  <i>... copied method body ...</i>
+     * Object result = &lt;<i>returned value</i>&gt;
+     * return (<i>&lt;returnType&gt;</i>)result;
+     * </pre></ul>
+     *
+     * <p>The variables <code>p0</code>, <code>p2</code>, ... represent
+     * formal parameters of the created method.
+     * The value of <code>cvalue</code> is specified by
+     * <code>constParam</code>.
+     *
+     * <p>If the type of a parameter or a returned value is a primitive
+     * type, then the value is converted into a wrapper object such as
+     * <code>java.lang.Integer</code>.  If the type of the returned value
+     * is <code>void</code>, the returned value is discarded.
+     *
+     * <p><i>Example:</i>
+     *
+     * <ul><pre>ClassPool pool = ... ;
+     * CtClass vec = pool.makeClass("intVector");
+     * vec.setSuperclass(pool.get("java.util.Vector"));
+     * CtMethod addMethod = pool.getMethod("Sample", "add0");
+     *
+     * CtClass[] argTypes = { CtClass.intType };
+     * CtMethod m = CtNewMethod.wrapped(CtClass.voidType, "add", argTypes,
+     *                                  null, addMethod, null, vec);
+     * vec.addMethod(m);</pre></ul>
+     *
+     * <p>where the class <code>Sample</code> is as follows:
+     *
+     * <ul><pre>public class Sample extends java.util.Vector {
+     *     public Object add0(Object[] args) {
+     *         super.addElement(args[0]);
+     *         return null;
+     *     }
+     * }</pre></ul>
+     *
+     * <p>This program produces a class <code>intVector</code>:
+     *
+     * <ul><pre>public class intVector extends java.util.Vector {
+     *     public void add(int p0) {
+     *         Object[] args = new Object[] { p0 };
+     *         // begin of copied body
+     *         super.addElement(args[0]);
+     *         Object result = null;
+     *         // end
+     *     }
+     * }</pre></ul>
+     *
+     * <p>Note that the type of the parameter to <code>add()</code> depends
+     * only on the value of <code>argTypes</code> passed to
+     * <code>CtNewMethod.wrapped()</code>.  Thus, it is easy to
+     * modify this program to produce a
+     * <code>StringVector</code> class, which is a vector containing
+     * only <code>String</code> objects, and other vector classes.
+     *
+     * @param returnType       the type of the returned value.
+     * @param mname            the method name.
+     * @param parameters       a list of the parameter types.
+     * @param exceptions       a list of the exception types.
+     * @param body             the method body
+     * @param constParam       the constant parameter
+     *                         (maybe <code>null</code>).
+     * @param declaring                the class to which the created method is
+     *                         added.
+     */
+    public static CtMethod wrapped(CtClass returnType,
+                                  String mname,
+                                  CtClass[] parameterTypes,
+                                  CtClass[] exceptionTypes,
+                                  CtMethod body, ConstParameter constParam,
+                                  CtClass declaring)
+       throws CannotCompileException
+    {
+       return CtNewWrappedMethod.wrapped(returnType, mname, parameterTypes,
+                       exceptionTypes, body, constParam, declaring);
+    }
+}
diff --git a/src/main/javassist/CtNewWrappedConstructor.java b/src/main/javassist/CtNewWrappedConstructor.java
new file mode 100644 (file)
index 0000000..fa4786e
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.CtMethod.ConstParameter;
+
+class CtNewWrappedConstructor extends CtNewWrappedMethod {
+    private static final int PASS_NONE = CtNewConstructor.PASS_NONE;
+    private static final int PASS_ARRAY = CtNewConstructor.PASS_ARRAY;
+    private static final int PASS_PARAMS = CtNewConstructor.PASS_PARAMS;
+
+    public static CtConstructor wrapped(CtClass[] parameterTypes,
+                                       CtClass[] exceptionTypes,
+                                       int howToCallSuper,
+                                       CtMethod body,
+                                       ConstParameter constParam,
+                                       CtClass declaring)
+       throws CannotCompileException
+    {
+       try {
+           CtConstructor cons = new CtConstructor(parameterTypes, declaring);
+           cons.setExceptionTypes(exceptionTypes);
+           Bytecode code = makeBody(declaring, declaring.getClassFile2(),
+                                    howToCallSuper, body,
+                                    parameterTypes, constParam);
+           cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
+           return cons;
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    protected static Bytecode makeBody(CtClass declaring, ClassFile classfile,
+                                      int howToCallSuper,
+                                      CtMethod wrappedBody,
+                                      CtClass[] parameters,
+                                      ConstParameter cparam)
+       throws CannotCompileException
+    {
+       int stacksize, stacksize2;
+
+       int superclazz = classfile.getSuperclassId();
+       Bytecode code = new Bytecode(classfile.getConstPool(), 0, 0);
+       code.setMaxLocals(false, parameters, 0);
+       code.addAload(0);
+       if (howToCallSuper == PASS_NONE) {
+           stacksize = 1;
+           code.addInvokespecial(superclazz, "<init>", "()V");
+       }
+       else if (howToCallSuper == PASS_PARAMS) {
+           stacksize = code.addLoadParameters(parameters) + 1;
+           code.addInvokespecial(superclazz, "<init>",
+                                 Descriptor.ofConstructor(parameters));
+       }
+       else {
+           stacksize = compileParameterList(code, parameters, 1);
+           String desc;
+           if (cparam == null) {
+               stacksize2 = 2;
+               desc = ConstParameter.defaultConstDescriptor();
+           }
+           else {
+               stacksize2 = cparam.compile(code) + 2;
+               desc = cparam.constDescriptor();
+           }
+
+           if (stacksize < stacksize2)
+               stacksize = stacksize2;
+
+           code.addInvokespecial(superclazz, "<init>", desc);
+       }
+
+       if (wrappedBody == null)
+           code.add(Bytecode.RETURN);
+       else {
+           stacksize2 = makeBody0(declaring, classfile, wrappedBody,
+                                  false, parameters, CtClass.voidType,
+                                  cparam, code);
+           if (stacksize < stacksize2)
+               stacksize = stacksize2;
+       }
+
+       code.setMaxStack(stacksize);
+       return code;
+    }
+}
diff --git a/src/main/javassist/CtNewWrappedMethod.java b/src/main/javassist/CtNewWrappedMethod.java
new file mode 100644 (file)
index 0000000..718e3c4
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.*;
+import javassist.compiler.JvstCodeGen;
+import java.util.Hashtable;
+import javassist.CtMethod.ConstParameter;
+
+class CtNewWrappedMethod {
+
+    private static final String addedWrappedMethod = "_added_m$";
+
+    public static CtMethod wrapped(CtClass returnType, String mname,
+                                  CtClass[] parameterTypes,
+                                  CtClass[] exceptionTypes,
+                                  CtMethod body, ConstParameter constParam,
+                                  CtClass declaring)
+       throws CannotCompileException
+    {
+       CtMethod mt = new CtMethod(returnType, mname, parameterTypes,
+                                  declaring);
+       mt.setModifiers(body.getModifiers());
+       try {
+           mt.setExceptionTypes(exceptionTypes);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+
+       Bytecode code = makeBody(declaring, declaring.getClassFile2(), body,
+                                parameterTypes, returnType, constParam);
+       mt.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
+       return mt;
+    }
+
+    static Bytecode makeBody(CtClass clazz, ClassFile classfile,
+                            CtMethod wrappedBody,
+                            CtClass[] parameters,
+                            CtClass returnType,
+                            ConstParameter cparam)
+       throws CannotCompileException
+    {
+       boolean isStatic = Modifier.isStatic(wrappedBody.getModifiers());
+       Bytecode code = new Bytecode(classfile.getConstPool(), 0, 0);
+       int stacksize = makeBody0(clazz, classfile, wrappedBody, isStatic,
+                                 parameters, returnType, cparam, code);
+       code.setMaxStack(stacksize);
+       code.setMaxLocals(isStatic, parameters, 0);
+       return code;
+    }
+
+    protected static int makeBody0(CtClass clazz, ClassFile classfile,
+                                  CtMethod wrappedBody,
+                                  boolean isStatic, CtClass[] parameters,
+                                  CtClass returnType, ConstParameter cparam,
+                                  Bytecode code)
+       throws CannotCompileException
+    {
+       if (!(clazz instanceof CtClassType))
+           throw new CannotCompileException("bad declaring class"
+                                            + clazz.getName());
+
+       if (!isStatic)
+           code.addAload(0);
+
+       int stacksize = compileParameterList(code, parameters,
+                                            (isStatic ? 0 : 1));
+       int stacksize2;
+       String desc;
+       if (cparam == null) {
+           stacksize2 = 0;
+           desc = ConstParameter.defaultDescriptor();
+       }
+       else {
+           stacksize2 = cparam.compile(code);
+           desc = cparam.descriptor();
+       }
+
+       checkSignature(wrappedBody, desc);
+
+       String bodyname;
+       try {
+           bodyname = addBodyMethod((CtClassType)clazz, classfile,
+                                    wrappedBody);
+           /* if an exception is thrown below, the method added above
+            * should be removed. (future work :<)
+            */
+       }
+       catch (BadBytecode e) {
+           throw new CannotCompileException(e);
+       }
+
+       if (isStatic)
+           code.addInvokestatic(Bytecode.THIS, bodyname, desc);
+       else
+           code.addInvokespecial(Bytecode.THIS, bodyname, desc);
+
+       compileReturn(code, returnType);        // consumes 2 stack entries
+
+       if (stacksize < stacksize2 + 2)
+           stacksize = stacksize2 + 2;
+
+       return stacksize;
+    }
+
+    private static void checkSignature(CtMethod wrappedBody,
+                                      String descriptor)
+       throws CannotCompileException
+    {
+       if (!descriptor.equals(wrappedBody.getMethodInfo2().getDescriptor()))
+           throw new CannotCompileException(
+                       "wrapped method with a bad signature: "
+                       + wrappedBody.getDeclaringClass().getName()
+                       + '.' + wrappedBody.getName());
+    }
+
+    private static String addBodyMethod(CtClassType clazz,
+                                       ClassFile classfile,
+                                       CtMethod src)
+       throws BadBytecode
+    {
+       Hashtable bodies = clazz.getHiddenMethods();
+       String bodyname = (String)bodies.get(src);
+       if (bodyname == null) {
+           do {
+               bodyname = addedWrappedMethod + clazz.getUniqueNumber();
+           } while (classfile.getMethod(bodyname) != null);
+
+           ClassMap map = new ClassMap();
+           map.put(src.getDeclaringClass().getName(), clazz.getName());
+           MethodInfo body = new MethodInfo(classfile.getConstPool(),
+                                            bodyname, src.getMethodInfo2(),
+                                            map);
+           int acc = body.getAccessFlags();
+           body.setAccessFlags(AccessFlag.setPrivate(acc));
+           classfile.addMethod(body);
+           bodies.put(src, bodyname);
+       }
+
+       return bodyname;
+    }
+
+    /* compileParameterList() returns the stack size used
+     * by the produced code.
+     *
+     * @param regno    the index of the local variable in which
+     *                 the first argument is received.
+     *                 (0: static method, 1: regular method.)
+     */
+    static int compileParameterList(Bytecode code,
+                                   CtClass[] params, int regno) {
+       return JvstCodeGen.compileParameterList(code, params, regno);
+    }
+
+    /*
+     * The produced codes cosume 1 or 2 stack entries.
+     */
+    private static void compileReturn(Bytecode code, CtClass type) {
+       if (type.isPrimitive()) {
+           CtPrimitiveType pt = (CtPrimitiveType)type;
+           if (pt != CtClass.voidType) {
+               String wrapper = pt.getWrapperName();
+               code.addCheckcast(wrapper);
+               code.addInvokevirtual(wrapper, pt.getGetMethodName(),
+                                     pt.getGetMethodDescriptor());
+           }
+
+           code.addOpcode(pt.getReturnOp());
+       }
+       else {
+           code.addCheckcast(type);
+           code.addOpcode(Bytecode.ARETURN);
+       }
+    }
+}
diff --git a/src/main/javassist/CtPrimitiveType.java b/src/main/javassist/CtPrimitiveType.java
new file mode 100644 (file)
index 0000000..8e73ca8
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+/**
+ * An instance of <code>CtPrimitiveType</code> represents a primitive type.
+ * It is obtained from <code>CtClass</code>.
+ */
+public final class CtPrimitiveType extends CtClass {
+    private char descriptor;
+    private String wrapperName;
+    private String getMethodName;
+    private String mDescriptor;
+    private int returnOp;
+    private int arrayType;
+    private int dataSize;
+
+    CtPrimitiveType(String name, char desc, String wrapper,
+                   String methodName, String mDesc, int opcode, int atype,
+                   int size) {
+       super(name);
+       descriptor = desc;
+       wrapperName = wrapper;
+       getMethodName = methodName;
+       mDescriptor = mDesc;
+       returnOp = opcode;
+       arrayType = atype;
+       dataSize = size;
+    }
+
+    /**
+     * Returns <code>true</code> if this object represents a primitive
+     * Java type: boolean, byte, char, short, int, long, float, double,
+     * or void.
+     */
+    public boolean isPrimitive() { return true; }
+
+    /**
+     * Returns the descriptor representing this type.
+     * For example, if the type is int, then the descriptor is I.
+     */
+    public char getDescriptor() { return descriptor; }
+
+    /**
+     * Returns the name of the wrapper class.
+     * For example, if the type is int, then the wrapper class is
+     * <code>java.lang.Integer</code>.
+     */
+    public String getWrapperName() { return wrapperName; }
+
+    /**
+     * Returns the name of the method for retrieving the value
+     * from the wrapper object.
+     * For example, if the type is int, then the method name is
+     * <code>intValue</code>.
+     */
+    public String getGetMethodName() { return getMethodName; }
+
+    /**
+     * Returns the descriptor of the method for retrieving the value
+     * from the wrapper object.
+     * For example, if the type is int, then the method descriptor is
+     * <code>()I</code>.
+     */
+    public String getGetMethodDescriptor() { return mDescriptor; }
+
+    /**
+     * Returns the opcode for returning a value of the type.
+     * For example, if the type is int, then the returned opcode is
+     * <code>javassit.bytecode.Opcode.IRETURN</code>.
+     */
+    public int getReturnOp() { return returnOp; }
+
+    /**
+     * Returns the array-type code representing the type.
+     * It is used for the newarray instruction.
+     * For example, if the type is int, then this method returns
+     * <code>javassit.bytecode.Opcode.T_INT</code>.
+     */
+    public int getArrayType() { return arrayType; }
+
+    /**
+     * Returns the data size of the primitive type.
+     * If the type is long or double, this method returns 2.
+     * Otherwise, it returns 1.
+     */
+    public int getDataSize() { return dataSize; }
+}
diff --git a/src/main/javassist/Dump.java b/src/main/javassist/Dump.java
new file mode 100644 (file)
index 0000000..afc9106
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.*;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ClassFileWriter;
+
+/**
+ * Dump is a tool for viewing the class definition in the given
+ * class file.  Unlike the JDK javap tool, Dump works even if
+ * the class file is broken.
+ *
+ * <p>For example,
+ * <ul><pre>% java javassist.Dump foo.class</pre></ul>
+ *
+ * <p>prints the contents of the constant pool and the list of methods
+ * and fields.
+ */
+public class Dump {
+    private Dump() {}
+
+    /**
+     * Main method.
+     *
+     * @param args[0]          class file name.
+     */
+    public static void main(String[] args) throws Exception {
+       if (args.length != 1) {
+           System.err.println("Usage: java Dump <class file name>");
+           return;
+       }
+
+       DataInputStream in = new DataInputStream(
+                                        new FileInputStream(args[0]));
+       ClassFile w = new ClassFile(in);
+       PrintWriter out = new PrintWriter(System.out, true);
+       out.println("*** constant pool ***");
+       w.getConstPool().print(out);
+       out.println();
+       out.println("*** members ***");
+       ClassFileWriter.print(w, out);
+    }
+}
diff --git a/src/main/javassist/Loader.java b/src/main/javassist/Loader.java
new file mode 100644 (file)
index 0000000..948d3de
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.*;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * The class loader for Javassist (JDK 1.2 or later only).
+ *
+ * <p>This is a sample class loader using <code>ClassPool</code>.
+ * Unlike a regular class loader, this class loader obtains bytecode
+ * from a <code>ClassPool</code>.
+ *
+ * <p>Note that Javassist can be used without this class loader; programmers
+ * can define their own versions of class loader.  They can run
+ * a program even without any user-defined class loader if that program
+ * is statically translated with Javassist.
+ * This class loader is just provided as a utility class.
+ *
+ * <p>Suppose that an instance of <code>MyTranslator</code> implementing
+ * the interface <code>Translator</code> is responsible for modifying
+ * class files.
+ * The startup program of an application using <code>MyTranslator</code>
+ * should be something like this:
+ *
+ * <ul><pre>
+ * import javassist.*;
+ *
+ * public class Main {
+ *   public static void main(String[] args) throws Throwable {
+ *     MyTranslator myTrans = new MyTranslator();
+ *     ClassPool cp = ClassPool.getDefault(myTrans);
+ *     Loader cl = new Loader(cp);
+ *     cl.run("MyApp", args);
+ *   }
+ * }
+ * </pre></ul>
+ *
+ * <p>Class <code>MyApp</code> is the main program of the application.
+ *
+ * <p>This program should be executed as follows:
+ *
+ * <ul><pre>
+ * % java Main <i>arg1</i> <i>arg2</i>...
+ * </pre></ul>
+ *
+ * <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code>
+ * object before loading it into the JVM.
+ * Then it calls <code>main()</code> in <code>MyApp</code> with arguments
+ * <i>arg1</i>, <i>arg2</i>, ...
+ *
+ * <p>This program execution is equivalent to:
+ *
+ * <ul><pre>
+ * % java MyApp <i>arg1</i> <i>arg2</i>...
+ * </pre></ul>
+ *
+ * <p>except that classes are translated by <code>MyTranslator</code>
+ * at load time.
+ *
+ * <p>If only a particular class is modified when it is loaded,
+ * a program like the following can be used:
+ *
+ * <ul><pre>ClassPool cp = ClassPool.getDefault();
+ * Loader cl = new Loader(cp);
+ *
+ * CtClass ct = cp.get("test.Rectangle");
+ * ct.setSuperclass(loader.get("test.Point"));
+ *
+ * Class c = cl.loadClass("test.Rectangle");
+ * Object rect = c.newInstance();</pre></ul>
+ *
+ * <p>This program modifies the super class of the <code>Rectangle</code>
+ * class and loads it into the JVM with a class loader <code>cl</code>.
+ *
+ * <p><b>Note 1:</b>
+ *
+ * <p>This class loader does not allow the users to intercept the loading
+ * of <code>java.*</code> and <code>javax.*</code> classes unless
+ * <code>Loader.doDelegation</code> is <code>false</code>.  Also see
+ * Note 2.
+ *
+ * <p><b>Note 2:</b>
+ *
+ * <p>If classes are loaded with different class loaders, they belong to
+ * separate name spaces.  If class <code>C</code> is loaded by a class
+ * loader <code>CL</code>, all classes that the class <code>C</code>
+ * refers to are also loaded by <code>CL</code>.  However, if <code>CL</code>
+ * delegates the loading of the class <code>C</code> to <code>CL'</code>,
+ * then those classes that the class <code>C</code> refers to
+ * are loaded by a parent class loader <code>CL'</code>
+ * instead of <code>CL</code>.
+ *
+ * <p>If an object of class <code>C</code> is assigned
+ * to a variable of class <code>C</code> belonging to a different name
+ * space, then a <code>ClassCastException</code> is thrown.
+ *
+ * <p>Because of the fact above, this loader delegates only the loading of
+ * <code>javassist.Loader</code>
+ * and classes included in package <code>java.*</code> and
+ * <code>javax.*</code> to the parent class
+ * loader.  Other classes are directly loaded by this loader.
+ *
+ * <p>For example, suppose that <code>java.lang.String</code> would be loaded
+ * by this loader while <code>java.io.File</code> is loaded by the parent
+ * class loader.  If the constructor of <code>java.io.File</code> is called
+ * with an instance of <code>java.lang.String</code>, then it may throw
+ * an exception since it accepts an instance of only the
+ * <code>java.lang.String</code> loaded by the parent class loader.
+ *
+ * @see javassist.ClassPool
+ * @see javassist.Translator
+ */
+public class Loader extends ClassLoader {
+    private Hashtable notDefinedHere;  // must be atomic.
+    private Vector notDefinedPackages; // must be atomic.
+    private ClassPool source;
+
+    /**
+     * Specifies the algorithm of class loading.
+     *
+     * <p>This class loader uses the parent class loader for
+     * <code>java.*</code> and <code>javax.*</code> classes.
+     * If this variable <code>doDelegation</code>
+     * is <code>false</code>, this class loader does not delegate those
+     * classes to the parent class loader.
+     *
+     * <p>The default value is <code>true</code>.
+     */
+    public boolean doDelegation = true;
+
+    /**
+     * Creates a new class loader.
+     */
+    public Loader() {
+       this(null);
+    }
+
+    /**
+     * Creates a new class loader.
+     *
+     * @param cp       the source of class files.
+     */
+    public Loader(ClassPool cp) {
+       notDefinedHere = new Hashtable();
+       notDefinedPackages = new Vector();
+       source = cp;
+       delegateLoadingOf("javassist.Loader");
+    }
+
+    /**
+     * Records a class so that the loading of that class is delegated
+     * to the parent class loader.
+     *
+     * <p>If the given class name ends with <code>.</code> (dot), then
+     * that name is interpreted as a package name.  All the classes
+     * in that package and the sub packages are delegated.
+     */
+    public void delegateLoadingOf(String classname) {
+       if (classname.endsWith("."))
+           notDefinedPackages.addElement(classname);
+       else
+           notDefinedHere.put(classname, this);
+    }
+
+    /**
+     * Sets the soruce <code>ClassPool</code>.
+     */
+    public void setClassPool(ClassPool cp) {
+       source = cp;
+    }
+
+    /**
+     * Loads a class with an instance of <code>Loader</code>
+     * and calls <code>main()</code> of that class.
+     *
+     * <p>This method calls <code>run()</code>.
+     *
+     * @param args[0]          class name to be loaded.
+     * @param args[1-n]                parameters passed to <code>main()</code>.
+     *
+     * @see javassist.Loader#run(String[])
+     */
+    public static void main(String[] args) throws Throwable {
+       Loader cl = new Loader();
+       cl.run(args);
+    }
+
+    /**
+     * Loads a class and calls <code>main()</code> in that class.
+     *
+     * @param args[0]          the name of the loaded class.
+     * @param args[1-n]                parameters passed to <code>main()</code>.
+     */
+    public void run(String[] args) throws Throwable {
+       int n = args.length - 1;
+       if (n >= 0) {
+           String[] args2 = new String[n];
+           for (int i = 0; i < n; ++i)
+               args2[i] = args[i + 1];
+
+           run(args[0], args2);
+       }
+    }
+
+    /**
+     * Loads a class and calls <code>main()</code> in that class.
+     *
+     * @param classname                the loaded class.
+     * @param args             parameters passed to <code>main()</code>.
+     */
+    public void run(String classname, String[] args) throws Throwable {
+       Class c = loadClass(classname);
+       try {
+           c.getDeclaredMethod("main", new Class[] { String[].class })
+               .invoke(null, new Object[] { args });
+       }
+       catch (java.lang.reflect.InvocationTargetException e) {
+           throw e.getTargetException();
+       }
+    }
+
+    /**
+     * Requests the class loader to load a class.
+     */
+    protected Class loadClass(String name, boolean resolve)
+       throws ClassFormatError, ClassNotFoundException
+    {
+       Class c = findLoadedClass(name);
+       if (c == null)
+           c = loadClassByDelegation(name);
+
+       if (c == null)
+           c = findClass(name);
+
+       if (c == null)
+           c = delegateToParent(name);
+
+       if (resolve)
+           resolveClass(c);
+
+       return c;
+    }
+
+    /**
+     * Finds the specified class using <code>ClassPath</code>.
+     * If the source throws an exception, this returns null.
+     *
+     * <p>This method can be overridden by a subclass of
+     * <code>Loader</code>.
+     */
+    protected Class findClass(String name) {
+       byte[] classfile;
+       try {
+           if (source != null)
+               classfile = source.write(name);
+           else {
+               String jarname = "/" + name.replace('.', '/') + ".class";
+               InputStream in = this.getClass().getResourceAsStream(jarname);
+
+               classfile = ClassPoolTail.readStream(in);
+           }
+       }
+       catch (Exception e) {
+           return null;
+       }
+
+       return defineClass(name, classfile, 0, classfile.length);
+    }
+
+    private Class loadClassByDelegation(String name)
+       throws ClassNotFoundException
+    {
+       /* The swing components must be loaded by a system
+        * class loader.
+        * javax.swing.UIManager loads a (concrete) subclass
+        * of LookAndFeel by a system class loader and cast
+        * an instance of the class to LookAndFeel for
+        * (maybe) a security reason.  To avoid failure of
+        * type conversion, LookAndFeel must not be loaded
+        * by this class loader.
+        */
+
+       Class c = null;
+       if (doDelegation)
+           if (name.startsWith("java.") || name.startsWith("javax.")
+               || name.startsWith("sun.") || name.startsWith("com.sun.")
+               || notDelegated(name))
+               c = delegateToParent(name);
+
+       return c;
+    }
+
+    private boolean notDelegated(String name) {
+       if (notDefinedHere.get(name) != null)
+           return true;
+
+       int n = notDefinedPackages.size();
+       for (int i = 0; i < n; ++i)
+           if (name.startsWith((String)notDefinedPackages.elementAt(i)))
+               return true;
+
+       return false;
+    }
+
+    private Class delegateToParent(String classname)
+       throws ClassNotFoundException
+    {
+       ClassLoader cl = getParent();
+       if (cl != null)
+           return cl.loadClass(classname);
+       else
+           return findSystemClass(classname);
+    }
+}
diff --git a/src/main/javassist/Modifier.java b/src/main/javassist/Modifier.java
new file mode 100644 (file)
index 0000000..e6c7d2c
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import javassist.bytecode.AccessFlag;
+
+/**
+ * The Modifier class provides static methods and constants to decode
+ * class and member access modifiers.  The constant values are equivalent
+ * to the corresponding values in <code>javassist.bytecode.AccessFlag</code>.
+ *
+ * <p>All the methods/constants in this class are compatible with
+ * ones in <code>java.lang.reflect.Modifier</code>.
+ *
+ * @see CtClass#getModifiers()
+ */
+public class Modifier {
+    public static final int PUBLIC    = AccessFlag.PUBLIC;
+    public static final int PRIVATE   = AccessFlag.PRIVATE;
+    public static final int PROTECTED = AccessFlag.PROTECTED;
+    public static final int STATIC    = AccessFlag.STATIC;
+    public static final int FINAL     = AccessFlag.FINAL;
+    public static final int SYNCHRONIZED = AccessFlag.SYNCHRONIZED;
+    public static final int VOLATILE  = AccessFlag.VOLATILE;
+    public static final int TRANSIENT = AccessFlag.TRANSIENT;
+    public static final int NATIVE    = AccessFlag.NATIVE;
+    public static final int INTERFACE = AccessFlag.INTERFACE;
+    public static final int ABSTRACT  = AccessFlag.ABSTRACT;
+    public static final int STRICT    = AccessFlag.STRICT;
+
+    /**
+     * Returns true if the modifiers include the <tt>public</tt>
+     * modifier.
+     */
+    public static boolean isPublic(int mod) {
+       return (mod & PUBLIC) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>private</tt>
+     * modifier.
+     */
+    public static boolean isPrivate(int mod) {
+       return (mod & PRIVATE) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>protected</tt>
+     * modifier.
+     */
+    public static boolean isProtected(int mod) {
+       return (mod & PROTECTED) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>static</tt>
+     * modifier.
+     */
+    public static boolean isStatic(int mod) {
+       return (mod & STATIC) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>final</tt>
+     * modifier.
+     */
+    public static boolean isFinal(int mod) {
+       return (mod & FINAL) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>synchronized</tt>
+     * modifier.
+     */
+    public static boolean isSynchronized(int mod) {
+       return (mod & SYNCHRONIZED) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>volatile</tt>
+     * modifier.
+     */
+    public static boolean isVolatile(int mod) {
+       return (mod & VOLATILE) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>transient</tt>
+     * modifier.
+     */
+    public static boolean isTransient(int mod) {
+       return (mod & TRANSIENT) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>native</tt>
+     * modifier.
+     */
+    public static boolean isNative(int mod) {
+       return (mod & NATIVE) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>interface</tt>
+     * modifier.
+     */
+    public static boolean isInterface(int mod) {
+       return (mod & INTERFACE) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>abstract</tt>
+     * modifier.
+     */
+    public static boolean isAbstract(int mod) {
+       return (mod & ABSTRACT) != 0;
+    }
+
+    /**
+     * Returns true if the modifiers include the <tt>strictfp</tt>
+     * modifier.
+     */
+    public static boolean isStrict(int mod) {
+       return (mod & STRICT) != 0;
+    }
+
+    /**
+     * Truns the public bit on.  The protected and private bits are
+     * cleared.
+     */
+    public static int setPublic(int mod) {
+       return (mod & ~(PRIVATE | PROTECTED)) | PUBLIC;
+    }
+
+    /**
+     * Truns the protected bit on.  The protected and public bits are
+     * cleared.
+     */
+    public static int setProtected(int mod) {
+       return (mod & ~(PRIVATE | PUBLIC)) | PROTECTED;
+    }
+
+    /**
+     * Truns the private bit on.  The protected and private bits are
+     * cleared.
+     */
+    public static int setPrivate(int mod) {
+       return (mod & ~(PROTECTED | PUBLIC)) | PRIVATE;
+    }
+
+    /**
+     * Clears the public, protected, and private bits.
+     */
+    public static int setPackage(int mod) {
+       return (mod & ~(PROTECTED | PUBLIC | PRIVATE));
+    }
+
+    /**
+     * Clears a specified bit in <code>mod</code>.
+     */
+    public static int clear(int mod, int clearBit) {
+       return mod & ~clearBit;
+    }
+
+    public static String toString(int mod) {
+       return java.lang.reflect.Modifier.toString(mod);
+    }
+}
diff --git a/src/main/javassist/NotFoundException.java b/src/main/javassist/NotFoundException.java
new file mode 100644 (file)
index 0000000..30b4b88
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+/**
+ * Signals that something could not be found.
+ */
+public class NotFoundException extends Exception {
+    public NotFoundException(String msg) {
+       super(msg);
+    }
+
+    public NotFoundException(String msg, Exception e) {
+       super(msg + " because of " + e.toString());
+    }
+}
diff --git a/src/main/javassist/SerialVersionUID.java b/src/main/javassist/SerialVersionUID.java
new file mode 100644 (file)
index 0000000..a16009c
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.*;
+import javassist.bytecode.*;
+import java.util.*;
+import java.security.*;
+
+/**
+ * Utility for calculating serialVersionUIDs for Serializable classes.
+ *
+ * @author Bob Lee (crazybob@crazybob.org)
+ * @author modified by Shigeru Chiba
+ */
+public class SerialVersionUID {
+
+    /**
+     * Adds serialVersionUID if one does not already exist. Call this before
+     * modifying a class to maintain serialization compatability.
+     */
+    public static void setSerialVersionUID(CtClass clazz)
+       throws CannotCompileException, NotFoundException
+    {
+       // check for pre-existing field.
+       try {
+           clazz.getDeclaredField("serialVersionUID");
+           return;
+       }
+       catch (NotFoundException e) {}
+
+       // check if the class is serializable.
+       if (!isSerializable(clazz))
+           return;
+            
+       // add field with default value.
+       CtField field = new CtField(CtClass.longType, "serialVersionUID",
+                                   clazz);
+       field.setModifiers(Modifier.PRIVATE | Modifier.STATIC |
+                          Modifier.FINAL);
+       clazz.addField(field, calculateDefault(clazz) + "L");
+    }
+
+    /**
+     * Does the class implement Serializable?
+     */
+    private static boolean isSerializable(CtClass clazz) 
+       throws NotFoundException
+    {
+        ClassPool pool = clazz.getClassPool();
+       return clazz.subtypeOf(pool.get("java.io.Serializable"));
+    }
+    
+    /**
+     * Calculate default value. See Java Serialization Specification, Stream
+     * Unique Identifiers.
+     */
+    static long calculateDefault(CtClass clazz)
+       throws CannotCompileException
+    {
+       try {
+            ByteArrayOutputStream bout = new ByteArrayOutputStream();
+            DataOutputStream out = new DataOutputStream(bout);
+            ClassFile classFile = clazz.getClassFile();
+            
+            // class name.
+            String javaName = javaName(clazz);
+            out.writeUTF(javaName);
+            
+            // class modifiers.
+            out.writeInt(clazz.getModifiers() & (Modifier.PUBLIC |
+                Modifier.FINAL | Modifier.INTERFACE | Modifier.ABSTRACT));
+           
+            // interfaces.
+            String[] interfaces = classFile.getInterfaces();
+            for (int i = 0; i < interfaces.length; i++)
+                interfaces[i] = javaName(interfaces[i]);
+
+            Arrays.sort(interfaces);
+            for (int i = 0; i < interfaces.length; i++)
+                out.writeUTF(interfaces[i]);
+            
+            // fields.
+            CtField[] fields = clazz.getDeclaredFields();
+            Arrays.sort(fields, new Comparator() {
+                public int compare(Object o1, Object o2) {
+                    CtField field1 = (CtField)o1;
+                    CtField field2 = (CtField)o2;
+                    return field1.getName().compareTo(field2.getName());
+                }
+            });
+
+            for (int i = 0; i < fields.length; i++) {
+                CtField field = (CtField) fields[i]; 
+                int mods = field.getModifiers();
+                if (((mods & Modifier.PRIVATE) == 0) ||
+                    ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) {
+                    out.writeUTF(field.getName());
+                    out.writeInt(mods);
+                    out.writeUTF(field.getFieldInfo2().getDescriptor());
+                }
+            }
+
+            // static initializer.
+            if (classFile.getStaticInitializer() != null) {
+                out.writeUTF("<clinit>");
+                out.writeInt(Modifier.STATIC);
+                out.writeUTF("()V");
+            }
+
+            // constructors.
+            CtConstructor[] constructors = clazz.getDeclaredConstructors();
+            Arrays.sort(constructors, new Comparator() {
+                public int compare(Object o1, Object o2) {
+                   CtConstructor c1 = (CtConstructor)o1;
+                   CtConstructor c2 = (CtConstructor)o2;
+                   return c1.getMethodInfo2().getDescriptor().compareTo(
+                                       c2.getMethodInfo2().getDescriptor());
+               }
+           });
+
+            for (int i = 0; i < constructors.length; i++) {
+                CtConstructor constructor = constructors[i];
+                int mods = constructor.getModifiers();
+                if ((mods & Modifier.PRIVATE) == 0) {
+                    out.writeUTF("<init>");
+                    out.writeInt(mods);
+                    out.writeUTF(constructor.getMethodInfo2()
+                                .getDescriptor().replace('/', '.'));
+                }
+            }
+
+            // methods.
+            CtMethod[] methods = clazz.getDeclaredMethods();
+            Arrays.sort(methods, new Comparator() {
+                public int compare(Object o1, Object o2) {
+                   CtMethod m1 = (CtMethod)o1;
+                   CtMethod m2 = (CtMethod)o2;
+                   int value = m1.getName().compareTo(m2.getName());
+                   if (value == 0)
+                       value = m1.getMethodInfo2().getDescriptor()
+                           .compareTo(m2.getMethodInfo2().getDescriptor());
+
+                   return value;
+               }
+            });
+
+            for (int i = 0; i < methods.length; i++) {
+                CtMethod method = methods[i];
+                int mods = method.getModifiers();
+                if ((mods & Modifier.PRIVATE) == 0) {
+                    out.writeUTF(method.getName());
+                    out.writeInt(mods);
+                    out.writeUTF(method.getMethodInfo2()
+                                .getDescriptor().replace('/', '.'));
+                }
+            }
+
+            // calculate hash.
+            out.flush();
+            MessageDigest digest = MessageDigest.getInstance("SHA");
+            byte[] digested = digest.digest(bout.toByteArray());
+            long hash = 0;
+            for (int i = Math.min(digested.length, 8) - 1; i >= 0; i--)
+                hash = (hash << 8) | (digested[i] & 0xFF);
+
+            return hash;
+       }
+       catch (IOException e) {
+           throw new CannotCompileException(e);
+       }
+       catch (NoSuchAlgorithmException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    private static String javaName(CtClass clazz) {
+        return Descriptor.toJavaName(Descriptor.toJvmName(clazz));
+    }
+
+    private static String javaName(String name) {
+        return Descriptor.toJavaName(Descriptor.toJvmName(name));
+    }
+}
diff --git a/src/main/javassist/Translator.java b/src/main/javassist/Translator.java
new file mode 100644 (file)
index 0000000..42599c9
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+/**
+ * An observer of <code>ClassPool</code>.
+ * The users can define a class implementing this
+ * interface and attach an instance of that class to a
+ * <code>ClassPool</code> object so that it can translate a class file
+ * when the class file is loaded into the JVM, for example.
+ *
+ * @see ClassPool#ClassPool(ClassPool,Translator)
+ * @see ClassPool#getDefault(Translator)
+ */
+public interface Translator {
+    /**
+     * Is invoked by a <code>ClassPool</code> for initialization
+     * when the object is attached to a <code>ClassPool</code> object.
+     *
+     * @param pool     the <code>ClassPool</code> that this translator
+     *                         is attached to.
+     *
+     * @see ClassPool#ClassPool(ClassPool,Translator)
+     * @see ClassPool#getDefault(Translator)
+     */
+    void start(ClassPool pool)
+       throws NotFoundException, CannotCompileException;
+
+    /**
+     * Is invoked by a <code>ClassPool</code> for notifying that
+     * a class is written out to an output stream.
+     *
+     * <p>If CtClass.frozen() is true, that is, if the class has been
+     * already modified and written, then onWrite() is not invoked.
+     *
+     * @param pool     the <code>ClassPool</code> that this translator
+     *                         is attached to.
+     * @param classname                a fully-qualified class name
+     *
+     * @see ClassPool#writeFile(String)
+     * @see ClassPool#writeFile(String, String)
+     * @see ClassPool#write(String)
+     * @see ClassPool#write(String,DataOutputStream)
+     */
+    void onWrite(ClassPool pool, String classname)
+       throws NotFoundException, CannotCompileException;
+}
diff --git a/src/main/javassist/URLClassPath.java b/src/main/javassist/URLClassPath.java
new file mode 100644 (file)
index 0000000..c359c77
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * A class search-path specified with URL (http).
+ *
+ * @see javassist.ClassPath
+ * @see ClassPool#insertClassPath(ClassPath)
+ * @see ClassPool#appendClassPath(ClassPath)
+ */
+public class URLClassPath implements ClassPath {
+    protected String hostname;
+    protected int port;
+    protected String directory;
+    protected String packageName;
+
+    /*
+     * Creates a search path specified with URL (http).
+     *
+     * <p>This search path is used only if a requested
+     * class name starts with the name specified by <code>packageName</code>.
+     * If <code>packageName</code> is "mypack" and a requested class is
+     * "mypack.sub.Test", then the given URL is used for loading that class.
+     * If <code>packageName</code> is <code>null</code>, the URL is used
+     * for loading any class.
+     *
+     * @param host             host name
+     * @param port             port number
+     * @param directory                directory name ending with "/".
+     *                         It can be "/" (root directory).
+     * @param packageName      package name.
+     */
+    public URLClassPath(String host, int port,
+                       String directory, String packageName) {
+       hostname = host;
+       this.port = port;
+       this.directory = directory;
+       this.packageName = packageName;
+    }
+
+    public String toString() {
+       return hostname + ":" + port + directory;
+    }
+
+    /**
+     * Opens a class file with http.
+     */
+    public InputStream openClassfile(String classname) {
+       try {
+           if (packageName == null || classname.startsWith(packageName)) {
+               String jarname
+                   = directory + classname.replace('.', '/') + ".class";
+               URLConnection con = fetchClass0(hostname, port, jarname);
+               return con.getInputStream();
+           }
+       }
+       catch (IOException e) {}
+       return null;    // not found
+    }
+
+    /**
+     * Reads a class file on an http server.
+     *
+     * @param host             host name
+     * @param port             port number
+     * @param directory                directory name ending with "/".
+     *                         It can be "/" (root directory).
+     * @param classname                fully-qualified class name
+     */
+    public static byte[] fetchClass(String host, int port,
+                                   String directory, String classname)
+       throws IOException
+    {
+       byte[] b;
+       URLConnection con = fetchClass0(host, port,
+               directory + classname.replace('.', '/') + ".class");
+       int size = con.getContentLength();
+       InputStream s = con.getInputStream();
+       if (size <= 0)
+           b = ClassPoolTail.readStream(s);
+       else {
+           b = new byte[size];
+           int len = 0;
+           do {
+               int n = s.read(b, len, size - len);
+               if (n < 0) {
+                   s.close();
+                   throw new IOException("the stream was closed: "
+                                         + classname);
+               }
+               len += n;
+           } while (len < size);
+       }
+
+       s.close();
+       return b;
+    }
+
+    private static URLConnection fetchClass0(String host, int port,
+                                            String filename)
+       throws IOException
+    {
+       URL url;
+       try {
+           url = new URL("http", host, port, filename);
+       }
+       catch (MalformedURLException e) {
+           // should never reache here.
+           throw new IOException("invalid URL?");
+       }
+
+       URLConnection con = url.openConnection();
+       con.connect();
+       return con;
+    }
+}
diff --git a/src/main/javassist/bytecode/AccessFlag.java b/src/main/javassist/bytecode/AccessFlag.java
new file mode 100644 (file)
index 0000000..c964ca7
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+/**
+ * A support class providing static methods and constants
+ * for access modifiers such as public, rivate, ...
+ */
+public class AccessFlag {
+    public static final int PUBLIC    = 0x0001;
+    public static final int PRIVATE   = 0x0002;
+    public static final int PROTECTED = 0x0004;
+    public static final int STATIC    = 0x0008;
+    public static final int FINAL     = 0x0010;
+    public static final int SYNCHRONIZED = 0x0020;
+    public static final int VOLATILE  = 0x0040;
+    public static final int TRANSIENT = 0x0080;
+    public static final int NATIVE    = 0x0100;
+    public static final int INTERFACE = 0x0200;
+    public static final int ABSTRACT  = 0x0400;
+    public static final int STRICT    = 0x0800;
+    public static final int SUPER     = 0x0020;
+
+    // Note: 0x0020 is assigned to both ACC_SUPER and ACC_SYNCHRONIZED
+    // although java.lang.reflect.Modifier does not recognize ACC_SUPER.
+
+    /**
+     * Truns the public bit on.  The protected and private bits are
+     * cleared.
+     */
+    public static int setPublic(int accflags) {
+       return (accflags & ~(PRIVATE | PROTECTED)) | PUBLIC;
+    }
+
+    /**
+     * Truns the protected bit on.  The protected and public bits are
+     * cleared.
+     */
+    public static int setProtected(int accflags) {
+       return (accflags & ~(PRIVATE | PUBLIC)) | PROTECTED;
+    }
+
+    /**
+     * Truns the private bit on.  The protected and private bits are
+     * cleared.
+     */
+    public static int setPrivate(int accflags) {
+       return (accflags & ~(PROTECTED | PUBLIC)) | PRIVATE;
+    }
+
+    /**
+     * Clears the public, protected, and private bits.
+     */
+    public static int setPackage(int accflags) {
+       return (accflags & ~(PROTECTED | PUBLIC | PRIVATE));
+    }
+
+    /**
+     * Clears a specified bit in <code>accflags</code>.
+     */
+    public static int clear(int accflags, int clearBit) {
+       return accflags & ~clearBit;
+    }
+
+    /**
+     * Converts a javassist.Modifier into
+     * a javassist.bytecode.AccessFlag.
+     *
+     * @param modifier         javassist.Modifier
+     */
+    public static int of(int modifier) {
+       return modifier;
+    }
+
+    /**
+     * Converts a javassist.bytecode.AccessFlag
+     * into a javassist.Modifier.
+     *
+     * @param accflags         javassist.bytecode.Accessflag
+     */
+    public static int toModifier(int accflags) {
+       return accflags;
+    }
+}
diff --git a/src/main/javassist/bytecode/AttributeInfo.java b/src/main/javassist/bytecode/AttributeInfo.java
new file mode 100644 (file)
index 0000000..29d7904
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+// Note: if you define a new subclass of AttributeInfo, then
+//      update AttributeInfo.read().
+
+/**
+ * <code>attribute_info</code> structure.
+ */
+public class AttributeInfo {
+    protected ConstPool constPool;
+    int name;
+    byte[] info;
+
+    protected AttributeInfo(ConstPool cp, int attrname, byte[] attrinfo) {
+       constPool = cp;
+       name = attrname;
+       info = attrinfo;
+    }
+
+    protected AttributeInfo(ConstPool cp, String attrname) {
+       this(cp, attrname, (byte[])null);
+    }
+
+    /**
+     * Constructs an <code>attribute_info</code> structure.
+     *
+     * @param cp               constant pool table
+     * @param attrname         attribute name
+     * @param attrinfo         <code>info</code> field
+     *                         of <code>attribute_info</code> structure.
+     */
+    public AttributeInfo(ConstPool cp, String attrname, byte[] attrinfo) {
+       this(cp, cp.addUtf8Info(attrname), attrinfo);
+    }
+
+    protected AttributeInfo(ConstPool cp, int n, DataInputStream in)
+       throws IOException
+    {
+       constPool = cp;
+       name = n;
+       int len = in.readInt();
+       info = new byte[len];
+       if (len > 0)
+           in.readFully(info);
+    }
+
+    static AttributeInfo read(ConstPool cp, DataInputStream in)
+       throws IOException
+    {
+       int name = in.readUnsignedShort();
+       String nameStr = cp.getUtf8Info(name);
+       if (nameStr.equals(CodeAttribute.tag))
+           return new CodeAttribute(cp, name, in);
+       else if (nameStr.equals(ExceptionsAttribute.tag))
+           return new ExceptionsAttribute(cp, name, in);
+       else if (nameStr.equals(ConstantAttribute.tag))
+           return new ConstantAttribute(cp, name, in);
+       else if (nameStr.equals(SourceFileAttribute.tag))
+           return new SourceFileAttribute(cp, name, in);
+       else if (nameStr.equals(LineNumberAttribute.tag))
+           return new LineNumberAttribute(cp, name, in);
+       else if (nameStr.equals(SyntheticAttribute.tag))
+           return new SyntheticAttribute(cp, name, in);
+       else if (nameStr.equals(InnerClassesAttribute.tag))
+           return new InnerClassesAttribute(cp, name, in);
+       else
+           return new AttributeInfo(cp, name, in);
+    }
+
+    /**
+     * Returns an attribute name.
+     */
+    public String getName() {
+       return constPool.getUtf8Info(name);
+    }
+
+    /**
+     * Returns a constant pool table.
+     */
+    public ConstPool getConstPool() { return constPool; }
+
+    /**
+     * Returns the length of this <code>attribute_info</code>
+     * structure.
+     * The returned value is <code>attribute_length + 6</code>.
+     */
+    public int length() {
+       return info.length + 6;
+    }
+
+    /**
+     * Returns the <code>info</code> field
+     * of this <code>attribute_info</code> structure.
+     *
+     * <p>This method is not available if the object is an instance
+     * of <code>CodeAttribute</code>.
+     */
+    public byte[] get() { return info; }
+
+    /**
+     * Sets the <code>info</code> field
+     * of this <code>attribute_info</code> structure.
+     *
+     * <p>This method is not available if the object is an instance
+     * of <code>CodeAttribute</code>.
+     */
+    public void set(byte[] newinfo) { info = newinfo; }
+
+    /**
+     * Makes a copy.  Class names are replaced according to the
+     * given <code>Map</code> object.
+     *
+     * @param newCp    the constant pool table used by the new copy.
+     * @param classnames       pairs of replaced and substituted
+     *                         class names.
+     */
+    public AttributeInfo copy(ConstPool newCp, Map classnames) {
+       int s = info.length;
+       byte[] newInfo = new byte[s];
+       for (int i = 0; i < s; ++i)
+           newInfo[i] = info[i];
+
+       return new AttributeInfo(newCp, getName(), newInfo);
+    }
+
+    void write(DataOutputStream out) throws IOException {
+       out.writeShort(name);
+       out.writeInt(info.length);
+       if (info.length > 0)
+           out.write(info);
+    }
+
+    static int getLength(LinkedList list) {
+       int size = 0;
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           AttributeInfo attr = (AttributeInfo)list.get(i);
+           size += attr.length();
+       }
+
+       return size;
+    }
+
+    static AttributeInfo lookup(LinkedList list, String name) {
+       if (list == null)
+           return null;
+
+       ListIterator iterator = list.listIterator();
+       while (iterator.hasNext()) {
+           AttributeInfo ai = (AttributeInfo)iterator.next();
+           if (ai.getName().equals(name))
+               return ai;
+       }
+
+       return null;            // no such attribute
+    }
+
+    static AttributeInfo lookup(LinkedList list, Class type) {
+       if (list == null)
+           return null;
+
+       ListIterator iterator = list.listIterator();
+       while (iterator.hasNext()) {
+           Object obj = iterator.next();
+           if (type.isInstance(obj))
+               return (AttributeInfo)obj;
+       }
+
+       return null;            // no such attribute
+    }
+
+    static synchronized void remove(LinkedList list, String name) {
+       if (list == null)
+           return;
+
+       ListIterator iterator = list.listIterator();
+       while (iterator.hasNext()) {
+           AttributeInfo ai = (AttributeInfo)iterator.next();
+           if (ai.getName().equals(name))
+               iterator.remove();
+       }
+    }
+
+    static synchronized void remove(LinkedList list, Class type) {
+       if (list == null)
+           return;
+
+       ListIterator iterator = list.listIterator();
+       while (iterator.hasNext()) {
+           Object obj = iterator.next();
+           if (type.isInstance(obj))
+               iterator.remove();
+       }
+    }
+
+    static void writeAll(LinkedList list, DataOutputStream out)
+       throws IOException
+    {
+       if (list == null)
+           return;
+
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           AttributeInfo attr = (AttributeInfo)list.get(i);
+           attr.write(out);
+       }
+    }
+}
diff --git a/src/main/javassist/bytecode/BadBytecode.java b/src/main/javassist/bytecode/BadBytecode.java
new file mode 100644 (file)
index 0000000..51d287d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+/**
+ * Signals that a bad bytecode sequence has been found.
+ */
+public class BadBytecode extends Exception {
+    public BadBytecode(int opcode) {
+       super("bytecode " + opcode);
+    }
+
+    public BadBytecode(String msg) {
+       super(msg);
+    }
+}
diff --git a/src/main/javassist/bytecode/ByteArray.java b/src/main/javassist/bytecode/ByteArray.java
new file mode 100644 (file)
index 0000000..e10231e
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+/**
+ * A collection of static methods for reading and writing a byte array.
+ */
+public class ByteArray {
+    /**
+     * Reads an unsigned 16bit integer at the index.
+     */
+    public static int readU16bit(byte[] code, int index) {
+       return ((code[index] & 0xff) << 8) | (code[index + 1] & 0xff);
+    }
+
+    /**
+     * Reads a signed 16bit integer at the index.
+     */
+    public static int readS16bit(byte[] code, int index) {
+       return (code[index] << 8) | (code[index + 1] & 0xff);
+    }
+
+    /**
+     * Writes a 16bit integer at the index.
+     */
+    public static void write16bit(int value, byte[] code, int index) {
+       code[index] = (byte)(value >>> 8);
+       code[index + 1] = (byte)value;
+    }
+
+    /**
+     * Reads a 32bit integer at the index.
+     */
+    public static int read32bit(byte[] code, int index) {
+       return (code[index] << 24) | ((code[index + 1] & 0xff) << 16)
+              | ((code[index + 2] & 0xff) << 8) | (code[index + 3] & 0xff);
+    }
+
+    /**
+     * Writes a 32bit integer at the index.
+     */
+    public static void write32bit(int value, byte[] code, int index) {
+       code[index] = (byte)(value >>> 24);
+       code[index + 1] = (byte)(value >>> 16);
+       code[index + 2] = (byte)(value >>> 8);
+       code[index + 3] = (byte)value;
+    }
+
+    /**
+     * Copies a 32bit integer.
+     *
+     * @param src      the source byte array.
+     * @param isrc     the index into the source byte array.
+     * @param dest     the destination byte array.
+     * @param idest    the index into the destination byte array.
+     */
+    static void copy32bit(byte[] src, int isrc, byte[] dest, int idest) {
+       dest[idest] = src[isrc];
+       dest[idest + 1] = src[isrc + 1];
+       dest[idest + 2] = src[isrc + 2];
+       dest[idest + 3] = src[isrc + 3];
+    }
+}
diff --git a/src/main/javassist/bytecode/Bytecode.java b/src/main/javassist/bytecode/Bytecode.java
new file mode 100644 (file)
index 0000000..f903e4b
--- /dev/null
@@ -0,0 +1,1262 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.CtPrimitiveType;
+
+/**
+ * A utility class for producing a bytecode sequence.
+ *
+ * <p>A <code>Bytecode</code> object is an unbounded array
+ * containing bytecode.  For example,
+ *
+ * <ul><pre>ConstPool cp = ...;    // constant pool table
+ * Bytecode b = new Bytecode(cp, 1, 0);
+ * b.addIconst(3);
+ * b.addReturn(CtClass.intType);
+ * CodeAttribute ca = b.toCodeAttribute();</ul></pre>
+ *
+ * <p>This program produces a Code attribute including a bytecode
+ * sequence:
+ *
+ * <ul><pre>iconst_3
+ * ireturn</pre></ul>
+ *
+ * @see ConstPool
+ * @see CodeAttribute
+ */
+public class Bytecode implements Opcode {
+    /**
+     * Represents the <code>CtClass</code> file using the
+     * constant pool table given to this <code>Bytecode</code> object.
+     */
+    public static final CtClass THIS = ConstPool.THIS;
+
+    static final int bufsize = 64;
+    ConstPool constPool;
+    int maxStack, maxLocals;
+    ExceptionTable tryblocks;
+    Bytecode next;
+    byte[] buffer;
+    int num;
+
+    private int stackDepth;
+
+    /**
+     * Constructs a <code>Bytecode</code> object with an empty bytecode
+     * sequence.
+     *
+     * <p>The parameters <code>stacksize</code> and <code>localvars</code>
+     * specify initial values
+     * of <code>max_stack</code> and <code>max_locals</code>.
+     * They can be changed later.
+     *
+     * @param cp               constant pool table.
+     * @param stacksize                <code>max_stack</code>.
+     * @param localvars                <code>max_locals</code>.
+     */
+    public Bytecode(ConstPool cp, int stacksize, int localvars) {
+       this();
+       constPool = cp;
+       maxStack = stacksize;
+       maxLocals = localvars;
+       tryblocks = new ExceptionTable(cp);
+       stackDepth = 0;
+    }
+
+    /* used in add().
+     */
+    private Bytecode() {
+       buffer = new byte[bufsize];
+       num = 0;
+       next = null;
+    }
+
+    /**
+     * Gets a constant pool table.
+     */
+    public ConstPool getConstPool() { return constPool; }
+
+    /**
+     * Returns <code>exception_table</code>.
+     */
+    public ExceptionTable getExceptionTable() { return tryblocks; }
+
+    /**
+     * Converts to a <code>CodeAttribute</code>.
+     */
+    public CodeAttribute toCodeAttribute() {
+       return new CodeAttribute(constPool, maxStack, maxLocals,
+                                get(), tryblocks);
+    }
+
+    /**
+     * Returns the length of the bytecode sequence.
+     */
+    public int length() {
+       int len = 0;
+       Bytecode b = this;
+       while (b != null) {
+           len += b.num;
+           b = b.next;
+       }
+
+       return len;
+    }
+
+    private void copy(byte[] dest, int index) {
+       Bytecode b = this;
+       while (b != null) {
+           System.arraycopy(b.buffer, 0, dest, index, b.num);
+           index += b.num;
+           b = b.next;
+       }
+    }
+
+    /**
+     * Returns the produced bytecode sequence.
+     */
+    public byte[] get() {
+       byte[] b = new byte[length()];
+       copy(b, 0);
+       return b;
+    }
+
+    /**
+     * Gets <code>max_stack</code>.
+     */
+    public int getMaxStack() { return maxStack; }
+
+    /**
+     * Sets <code>max_stack</code>.
+     *
+     * <p>This value may be automatically updated when an instruction
+     * is appended.  A <code>Bytecode</code> object maintains the current
+     * stack depth whenever an instruction is added
+     * by <code>addOpcode()</code>.  For example, if DUP is appended,
+     * the current stack depth is increased by one.  If the new stack
+     * depth is more than <code>max_stack</code>, then it is assigned
+     * to <code>max_stack</code>.  However, if branch instructions are
+     * appended, the current stack depth may not be correctly maintained.
+     *
+     * @see #addOpcode(int)
+     */
+    public void setMaxStack(int size) {
+       maxStack = size;
+    }
+
+    /**
+     * Gets <code>max_locals</code>.
+     */
+    public int getMaxLocals() { return maxLocals; }
+
+    /**
+     * Sets <code>max_locals</code>.
+     */
+    public void setMaxLocals(int size) {
+       maxLocals = size;
+    }
+
+    /**
+     * Sets <code>max_locals</code>.
+     *
+     * <p>This computes the number of local variables
+     * used to pass method parameters and sets <code>max_locals</code>
+     * to that number plus <code>locals</code>.
+     *
+     * @param isStatic         true if <code>params</code> must be
+     *                         interpreted as parameters to a static method.
+     * @param params           parameter types.
+     * @param locals           the number of local variables excluding
+     *                         ones used to pass parameters.
+     */
+    public void setMaxLocals(boolean isStatic, CtClass[] params,
+                            int locals) {
+       if (!isStatic)
+           ++locals;
+
+       if (params != null) {
+           CtClass doubleType = CtClass.doubleType;
+           CtClass longType = CtClass.longType;
+           int n = params.length;
+           for (int i = 0; i < n; ++i) {
+               CtClass type = params[i];
+               if (type == doubleType || type == longType)
+                   locals += 2;
+               else
+                   ++locals;
+           }
+       }
+
+       maxLocals = locals;
+    }
+
+    /**
+     * Increments <code>max_locals</code>.
+     */
+    public void incMaxLocals(int diff) {
+       maxLocals += diff;
+    }
+
+    /**
+     * Adds a new entry of <code>exception_table</code>.
+     */
+    public void addExceptionHandler(int start, int end,
+                                   int handler, CtClass type) {
+       addExceptionHandler(start, end, handler,
+                           constPool.addClassInfo(type));
+    }
+
+    /**
+     * Adds a new entry of <code>exception_table</code>.
+     */
+    public void addExceptionHandler(int start, int end,
+                                   int handler, int type) {
+       tryblocks.add(start, end, handler, type);
+    }
+
+    /**
+     * Returns the length of bytecode sequence
+     * that have been added so far.
+     */
+    public int currentPc() {
+       int n = 0;
+       Bytecode b = this;
+       while (b != null) {
+           n += b.num;
+           b = b.next;
+       }
+
+       return n;
+    }
+
+    /**
+     * Reads a signed 8bit value at the offset from the beginning of the
+     * bytecode sequence.
+     *
+     * @throws ArrayIndexOutOfBoundsException  if offset is invalid.
+     */
+    public int read(int offset) {
+       if (offset < 0)
+           return Opcode.NOP;
+       else if (offset < num)
+           return buffer[offset];
+       else
+           try {
+               return next.read(offset - num);
+           }
+           catch (NullPointerException e) {
+               throw new ArrayIndexOutOfBoundsException(offset);
+           }
+    }
+
+    /**
+     * Reads a signed 16bit value at the offset from the beginning of the
+     * bytecode sequence.
+     */
+    public int read16bit(int offset) {
+       int v1 = read(offset);
+       int v2 = read(offset + 1);
+       return (v1 << 8) + (v2 & 0xff);
+    }
+
+    /**
+     * Writes an 8bit value at the offset from the beginning of the
+     * bytecode sequence.
+     *
+     * @throws ArrayIndexOutOfBoundsException  if offset is invalid.
+     */
+    public void write(int offset, int value) {
+       if (offset < num)
+           buffer[offset] = (byte)value;
+       else
+           try {
+               next.write(offset - num, value);
+           }
+           catch (NullPointerException e) {
+               throw new ArrayIndexOutOfBoundsException(offset);
+           }
+    }
+
+    /**
+     * Writes an 16bit value at the offset from the beginning of the
+     * bytecode sequence.
+     */
+    public void write16bit(int offset, int value) {
+       write(offset, value >>> 8);
+       write(offset + 1, value);
+    }
+
+    /**
+     * Appends an 8bit value to the end of the bytecode sequence.
+     */
+    public void add(int code) {
+       if (num < bufsize)
+           buffer[num++] = (byte)code;
+       else {
+           if (next == null)
+               next = new Bytecode();
+
+           next.add(code);
+       }
+    }
+
+    /**
+     * Appends an 8bit opcode to the end of the bytecode sequence.
+     * The current stack depth is updated.
+     * <code>max_stack</code> is updated if the current stack depth
+     * is the deepest so far.
+     *
+     * <p>Note: some instructions such as INVOKEVIRTUAL does not
+     * update the current stack depth since the increment depends
+     * on the method signature.
+     * <code>growStack()</code> must be explicitly called.
+     */
+    public void addOpcode(int code) {
+       add(code);
+       growStack(STACK_GROW[code]);
+    }
+
+    /**
+     * Increases the current stack depth.
+     * It also updates <code>max_stack</code> if the current stack depth
+     * is the deepest so far.
+     *
+     * @param diff     the number added to the current stack depth.
+     */
+    public void growStack(int diff) {
+       setStackDepth(stackDepth + diff);
+    }
+
+    /**
+     * Returns the current stack depth.
+     */
+    public int getStackDepth() { return stackDepth; }
+
+    /**
+     * Sets the current stack depth.
+     * It also updates <code>max_stack</code> if the current stack depth
+     * is the deepest so far.
+     *
+     * @param depth    new value.
+     */
+    public void setStackDepth(int depth) {
+       stackDepth = depth;
+       if (stackDepth > maxStack)
+           maxStack = stackDepth;
+    }
+
+    /**
+     * Appends a 16bit value to the end of the bytecode sequence.
+     * It never changes the current stack depth.
+     */
+    public void addIndex(int index) {
+       add(index >> 8);
+       add(index);
+    }
+
+    /**
+     * Appends ALOAD or (WIDE) ALOAD_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addAload(int n) {
+       if (n < 4)
+           addOpcode(42 + n);          // aload_<n>
+       else if (n < 0x100) {
+           addOpcode(ALOAD);           // aload
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(ALOAD);
+           addIndex(n);
+       }
+    }
+
+    /**
+     * Appends ASTORE or (WIDE) ASTORE_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addAstore(int n) {
+       if (n < 4)
+           addOpcode(75 + n);  // astore_<n>
+       else if (n < 0x100) {
+           addOpcode(ASTORE);          // astore
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(ASTORE);
+           addIndex(n);
+       }
+    }
+
+    /**
+     * Appends ICONST or ICONST_&lt;n&gt;
+     *
+     * @param n                the pushed integer constant.
+     */
+    public void addIconst(int n) {
+       if (n < 6 && -2 < n)
+           addOpcode(3 + n);           // iconst_<i>   -1..5
+       else if (n <= 127 && -128 <= n) {
+           addOpcode(16);              // bipush
+           add(n);
+       }
+       else if (n <= 32767 && -32768 <= n) {
+           addOpcode(17);              // sipush
+           add(n >> 8);
+           add(n);
+       }
+       else
+           addLdc(constPool.addIntegerInfo(n));
+    }
+
+    /**
+     * Appends ILOAD or (WIDE) ILOAD_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addIload(int n) {
+       if (n < 4)
+           addOpcode(26 + n);          // iload_<n>
+       else if (n < 0x100) {
+           addOpcode(ILOAD);           // iload
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(ILOAD);
+           addIndex(n);
+       }
+    }
+
+    /**
+     * Appends ISTORE or (WIDE) ISTORE_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addIstore(int n) {
+       if (n < 4)
+           addOpcode(59 + n);          // istore_<n>
+       else if (n < 0x100) {
+           addOpcode(ISTORE);          // istore
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(ISTORE);
+           addIndex(n);
+       }
+    }
+
+    /**
+     * Appends LCONST or LCONST_&lt;n&gt;
+     *
+     * @param n                the pushed long integer constant.
+     */
+    public void addLconst(long n) {
+       if (n == 0 || n == 1)
+           addOpcode(9 + (int)n);              // lconst_<n>
+       else
+           addLdc2w(n);
+    }
+
+    /**
+     * Appends LLOAD or (WIDE) LLOAD_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addLload(int n) {
+       if (n < 4)
+           addOpcode(30 + n);          // lload_<n>
+       else if (n < 0x100) {
+           addOpcode(LLOAD);           // lload
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(LLOAD);
+           addIndex(n);
+       }
+    }
+
+    /**
+     * Appends LSTORE or LSTORE_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addLstore(int n) {
+       if (n < 4)
+           addOpcode(63 + n);          // lstore_<n>
+       else if (n < 0x100) {
+           addOpcode(LSTORE);          // lstore
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(LSTORE);
+           addIndex(n);
+       }
+    }
+   
+    /**
+     * Appends DCONST or DCONST_&lt;n&gt;
+     *
+     * @param d                the pushed double constant.
+     */
+    public void addDconst(double d) {
+       if (d == 0.0 || d == 1.0)
+           addOpcode(14 + (int)d);             // dconst_<n>
+       else
+           addLdc2w(d);
+    }
+
+    /**
+     * Appends DLOAD or (WIDE) DLOAD_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addDload(int n) {
+       if (n < 4)
+           addOpcode(38 + n);          // dload_<n>
+       else if (n < 0x100) {
+           addOpcode(DLOAD);           // dload
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(DLOAD);
+           addIndex(n);
+       }
+    }
+
+    /**
+     * Appends DSTORE or (WIDE) DSTORE_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addDstore(int n) {
+       if (n < 4)
+           addOpcode(71 + n);          // dstore_<n>
+       else if (n < 0x100) {
+           addOpcode(DSTORE);          // dstore
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(DSTORE);
+           addIndex(n);
+       }
+    }
+
+    /**
+     * Appends FCONST or FCONST_&lt;n&gt;
+     *
+     * @param f                the pushed float constant.
+     */
+    public void addFconst(float f) {
+       if (f == 0.0f || f == 1.0f || f == 2.0f)
+           addOpcode(11 + (int)f);             // fconst_<n>
+       else
+           addLdc(constPool.addFloatInfo(f));
+    }
+
+    /**
+     * Appends FLOAD or (WIDE) FLOAD_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addFload(int n) {
+       if (n < 4)
+           addOpcode(34 + n);          // fload_<n>
+       else if (n < 0x100) {
+           addOpcode(FLOAD);           // fload
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(FLOAD);
+           addIndex(n);
+       }
+    }
+
+    /**
+     * Appends FSTORE or FSTORE_&lt;n&gt;
+     *
+     * @param n                an index into the local variable array.
+     */
+    public void addFstore(int n) {
+       if (n < 4)
+           addOpcode(67 + n);          // fstore_<n>
+       else if (n < 0x100) {
+           addOpcode(FSTORE);          // fstore
+           add(n);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(FSTORE);
+           addIndex(n);
+       }
+    }
+
+    /**
+     * Appends an instruction for loading a value from the
+     * local variable at the index <code>n</code>.
+     *
+     * @param n                the index.
+     * @param type     the type of the loaded value.
+     * @return         the size of the value (1 or 2 word).
+     */
+    public int addLoad(int n, CtClass type) {
+       if (type.isPrimitive()) {
+           if (type == CtClass.booleanType || type == CtClass.charType
+               || type == CtClass.byteType || type == CtClass.shortType
+               || type == CtClass.intType)
+               addIload(n);
+           else if (type == CtClass.longType) {
+               addLload(n);
+               return 2;
+           }
+           else if(type == CtClass.floatType)
+               addFload(n);
+           else if(type == CtClass.doubleType) {
+               addDload(n);
+               return 2;
+           }
+           else
+               throw new RuntimeException("void type?");
+       }
+       else
+           addAload(n);
+
+       return 1;
+    }
+
+    /**
+     * Appends an instruction for storing a value into the
+     * local variable at the index <code>n</code>.
+     *
+     * @param n                the index.
+     * @param type     the type of the stored value.
+     * @return         2 if the type is long or double.  Otherwise 1.
+     */
+    public int addStore(int n, CtClass type) {
+       if (type.isPrimitive()) {
+           if (type == CtClass.booleanType || type == CtClass.charType
+               || type == CtClass.byteType || type == CtClass.shortType
+               || type == CtClass.intType)
+               addIstore(n);
+           else if (type == CtClass.longType) {
+               addLstore(n);
+               return 2;
+           }
+           else if(type == CtClass.floatType)
+               addFstore(n);
+           else if(type == CtClass.doubleType) {
+               addDstore(n);
+               return 2;
+           }
+           else
+               throw new RuntimeException("void type?");
+       }
+       else
+           addAstore(n);
+
+       return 1;
+    }
+
+    /**
+     * Appends instructions for loading all the parameters onto the
+     * operand stack.
+     */
+    public int addLoadParameters(CtClass[] params) {
+       int stacksize = 0;
+       if (params != null) {
+           int n = params.length;
+           for (int i = 0; i < n; ++i)
+               stacksize += addLoad(stacksize + 1, params[i]);
+       }
+
+       return stacksize;
+    }
+
+    /**
+     * Appends CHECKCAST.
+     *
+     * @param c                the type.
+     */
+    public void addCheckcast(CtClass c) {
+       addOpcode(CHECKCAST);
+       addIndex(constPool.addClassInfo(c));
+    }
+
+    /**
+     * Appends CHECKCAST.
+     *
+     * @param classname                a fully-qualified class name.
+     */
+    public void addCheckcast(String classname) {
+       addOpcode(CHECKCAST);
+       addIndex(constPool.addClassInfo(classname));
+    }
+
+    /**
+     * Appends INSTANCEOF.
+     *
+     * @param classname                the class name.
+     */
+    public void addInstanceof(String classname) {
+       addOpcode(INSTANCEOF);
+       addIndex(constPool.addClassInfo(classname));
+    }
+
+    /**
+     * Appends GETFIELD.
+     *
+     * @param c                the class
+     * @param name     the field name
+     * @param type     the descriptor of the field type.
+     *
+     * @see Descriptor#of(CtClass)
+     */
+    public void addGetfield(CtClass c, String name, String type) {
+       add(GETFIELD);
+       int ci = constPool.addClassInfo(c);
+       addIndex(constPool.addFieldrefInfo(ci, name, type));
+       growStack(Descriptor.dataSize(type) - 1);
+    }
+
+    /**
+     * Appends GETSTATIC.
+     *
+     * @param c                the class
+     * @param name     the field name
+     * @param type     the descriptor of the field type.
+     *
+     * @see Descriptor#of(CtClass)
+     */
+    public void addGetstatic(CtClass c, String name, String type) {
+       add(GETSTATIC);
+       int ci = constPool.addClassInfo(c);
+       addIndex(constPool.addFieldrefInfo(ci, name, type));
+       growStack(Descriptor.dataSize(type));
+    }
+
+    /**
+     * Appends GETSTATIC.
+     *
+     * @param c                the fully-qualified class name
+     * @param name     the field name
+     * @param type     the descriptor of the field type.
+     *
+     * @see Descriptor#of(CtClass)
+     */
+    public void addGetstatic(String c, String name, String type) {
+       add(GETSTATIC);
+       int ci = constPool.addClassInfo(c);
+       addIndex(constPool.addFieldrefInfo(ci, name, type));
+       growStack(Descriptor.dataSize(type));
+    }
+
+    /**
+     * Appends INVOKESPECIAL.
+     *
+     * @param clazz    the target class.
+     * @param name     the method name.
+     * @param returnType       the return type.
+     * @param paramTypes       the parameter types.
+     */
+    public void addInvokespecial(CtClass clazz, String name,
+                                CtClass returnType, CtClass[] paramTypes) {
+       String desc = Descriptor.ofMethod(returnType, paramTypes);
+       addInvokespecial(clazz, name, desc);
+    }
+
+    /**
+     * Appends INVOKESPECIAL.
+     *
+     * @param clazz    the target class.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     * @see Descriptor#ofConstructor(CtClass[])
+     */
+    public void addInvokespecial(CtClass clazz, String name, String desc) {
+       addInvokespecial(constPool.addClassInfo(clazz), name, desc);
+    }
+
+    /**
+     * Appends INVOKESPECIAL.
+     *
+     * @param clazz    the fully-qualified class name.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     * @see Descriptor#ofConstructor(CtClass[])
+     */
+    public void addInvokespecial(String clazz, String name, String desc) {
+       addInvokespecial(constPool.addClassInfo(clazz), name, desc);
+    }
+
+    /**
+     * Appends INVOKESPECIAL.
+     *
+     * @param clazz    the index of <code>CONSTANT_Class_info</code>
+     *                 structure.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     * @see Descriptor#ofConstructor(CtClass[])
+     */
+    public void addInvokespecial(int clazz, String name, String desc) {
+       add(INVOKESPECIAL);
+       addIndex(constPool.addMethodrefInfo(clazz, name, desc));
+       growStack(Descriptor.dataSize(desc) - 1);
+    }
+
+    /**
+     * Appends INVOKESTATIC.
+     *
+     * @param clazz    the target class.
+     * @param name     the method name
+     * @param returnType       the return type.
+     * @param paramTypes       the parameter types.
+     */
+    public void addInvokestatic(CtClass clazz, String name,
+                               CtClass returnType, CtClass[] paramTypes) {
+       String desc = Descriptor.ofMethod(returnType, paramTypes);
+       addInvokestatic(clazz, name, desc);
+    }
+
+    /**
+     * Appends INVOKESTATIC.
+     *
+     * @param clazz    the target class.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     */
+    public void addInvokestatic(CtClass clazz, String name, String desc) {
+       addInvokestatic(constPool.addClassInfo(clazz), name, desc);
+    }
+
+    /**
+     * Appends INVOKESTATIC.
+     *
+     * @param classname        the fully-qualified class name.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     */
+    public void addInvokestatic(String classname, String name, String desc) {
+       addInvokestatic(constPool.addClassInfo(classname), name, desc);
+    }
+
+    /**
+     * Appends INVOKESTATIC.
+     *
+     * @param clazz    the index of <code>CONSTANT_Class_info</code>
+     *                 structure.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     */
+    public void addInvokestatic(int clazz, String name, String desc) {
+       add(INVOKESTATIC);
+       addIndex(constPool.addMethodrefInfo(clazz, name, desc));
+       growStack(Descriptor.dataSize(desc));
+    }
+
+    /**
+     * Appends INVOKEVIRTUAL.
+     *
+     * <p>The specified method must not be an inherited method.
+     * It must be directly declared in the class specified
+     * in <code>clazz</code>.
+     *
+     * @param clazz    the target class.
+     * @param name     the method name
+     * @param returnType       the return type.
+     * @param paramTypes       the parameter types.
+     */
+    public void addInvokevirtual(CtClass clazz, String name,
+                                CtClass returnType, CtClass[] paramTypes) {
+       String desc = Descriptor.ofMethod(returnType, paramTypes);
+       addInvokevirtual(clazz, name, desc);
+    }
+
+    /**
+     * Appends INVOKEVIRTUAL.
+     *
+     * <p>The specified method must not be an inherited method.
+     * It must be directly declared in the class specified
+     * in <code>clazz</code>.
+     *
+     * @param clazz    the target class.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     */
+    public void addInvokevirtual(CtClass clazz, String name, String desc) {
+       addInvokevirtual(constPool.addClassInfo(clazz), name, desc);
+    }
+
+    /**
+     * Appends INVOKEVIRTUAL.
+     *
+     * <p>The specified method must not be an inherited method.
+     * It must be directly declared in the class specified
+     * in <code>classname</code>.
+     *
+     * @param classname        the fully-qualified class name.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     */
+    public void addInvokevirtual(String classname, String name, String desc) {
+       addInvokevirtual(constPool.addClassInfo(classname), name, desc);
+    }
+
+    /**
+     * Appends INVOKEVIRTUAL.
+     *
+     * <p>The specified method must not be an inherited method.
+     * It must be directly declared in the class specified
+     * by <code>clazz</code>.
+     *
+     * @param clazz    the index of <code>CONSTANT_Class_info</code>
+     *                 structure.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     */
+    public void addInvokevirtual(int clazz, String name, String desc) {
+       add(INVOKEVIRTUAL);
+       addIndex(constPool.addMethodrefInfo(clazz, name, desc));
+       growStack(Descriptor.dataSize(desc) - 1);
+    }
+
+    /**
+     * Appends INVOKEINTERFACE.
+     *
+     * @param clazz    the target class.
+     * @param name     the method name
+     * @param returnType       the return type.
+     * @param paramTypes       the parameter types.
+     * @param count    the count operand of the instruction.
+     */
+    public void addInvokeinterface(CtClass clazz, String name,
+                                  CtClass returnType, CtClass[] paramTypes,
+                                  int count) {
+       String desc = Descriptor.ofMethod(returnType, paramTypes);
+       addInvokeinterface(clazz, name, desc, count);
+    }
+
+    /**
+     * Appends INVOKEINTERFACE.
+     *
+     * @param clazz    the target class.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     * @param count    the count operand of the instruction.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     */
+    public void addInvokeinterface(CtClass clazz, String name,
+                                  String desc, int count) {
+       addInvokeinterface(constPool.addClassInfo(clazz), name, desc,
+                          count);
+    }
+
+    /**
+     * Appends INVOKEINTERFACE.
+     *
+     * @param classname        the fully-qualified class name.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     * @param count    the count operand of the instruction.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     */
+    public void addInvokeinterface(String classname, String name,
+                                  String desc, int count) {
+       addInvokeinterface(constPool.addClassInfo(classname), name, desc,
+                          count);
+    }
+
+    /**
+     * Appends INVOKEINTERFACE.
+     *
+     * @param clazz    the index of <code>CONSTANT_Class_info</code>
+     *                 structure.
+     * @param name     the method name
+     * @param desc     the descriptor of the method signature.
+     * @param count    the count operand of the instruction.
+     *
+     * @see Descriptor#ofMethod(CtClass,CtClass[])
+     */
+    public void addInvokeinterface(int clazz, String name,
+                                  String desc, int count) {
+       add(INVOKEINTERFACE);
+       addIndex(constPool.addInterfaceMethodrefInfo(clazz, name, desc));
+       add(count);
+       add(0);
+       growStack(Descriptor.dataSize(desc) - 1);
+    }
+
+    /**
+     * Appends LDC or LDC_W.  The pushed item is a <code>String</code>
+     * object.
+     *
+     * @param s                the character string pushed by LDC or LDC_W.
+     */
+    public void addLdc(String s) {
+       addLdc(constPool.addStringInfo(s));
+    }
+
+    /**
+     * Appends LDC or LDC_W.
+     *
+     * @param i                index into the constant pool.
+     */
+    public void addLdc(int i) {
+       if (i > 0xFF) {
+           addOpcode(LDC_W);
+           addIndex(i);
+       }
+       else {
+           addOpcode(LDC);
+           add(i);
+       }
+    }
+
+    /**
+     * Appends LDC2_W.  The pushed item is a long value.
+     */
+    public void addLdc2w(long l) {
+       addOpcode(LDC2_W);
+       addIndex(constPool.addLongInfo(l));
+    }
+
+    /**
+     * Appends LDC2_W.  The pushed item is a double value.
+     */
+    public void addLdc2w(double d) {
+       addOpcode(LDC2_W);
+       addIndex(constPool.addDoubleInfo(d));
+    }
+
+    /**
+     * Appends NEW.
+     *
+     * @param clazz    the class of the created instance.
+     */
+    public void addNew(CtClass clazz) {
+       addOpcode(NEW);
+       addIndex(constPool.addClassInfo(clazz));
+    }
+
+    /**
+     * Appends NEW.
+     *
+     * @param classname                the fully-qualified class name.
+     */
+    public void addNew(String classname) {
+       addOpcode(NEW);
+       addIndex(constPool.addClassInfo(classname));
+    }
+
+    /**
+     * Appends ANEWARRAY.
+     *
+     * @param classname                the qualified class name of the element type.
+     */
+    public void addAnewarray(String classname) {
+       addOpcode(ANEWARRAY);
+       addIndex(constPool.addClassInfo(classname));
+    }
+
+    /**
+     * Appends ICONST and ANEWARRAY.
+     *
+     * @param clazz    the elememnt type.
+     * @param length   the array length.
+     */
+    public void addAnewarray(CtClass clazz, int length) {
+       addIconst(length);
+       addOpcode(ANEWARRAY);
+       addIndex(constPool.addClassInfo(clazz));
+    }
+
+    /**
+     * Appends NEWARRAY for primitive types.
+     *
+     * @param atype    <code>T_BOOLEAN</code>, <code>T_CHAR</code>, ...
+     * @see Opcode
+     */
+    public void addNewarray(int atype, int length) {
+       addIconst(length);
+       addOpcode(NEWARRAY);
+       add(atype);
+    }
+
+    /**
+     * Appends MULTINEWARRAY.
+     *
+     * @param clazz            the array type.
+     * @param dimensions       the sizes of all dimensions.
+     * @return         the length of <code>dimensions</code>.
+     */
+    public int addMultiNewarray(CtClass clazz, int[] dimensions) {
+       int len = dimensions.length;
+       for (int i = 0; i < len; ++i)
+           addIconst(dimensions[i]);
+
+       growStack(len);
+       return addMultiNewarray(clazz, len);
+    }
+
+    /**
+     * Appends MULTINEWARRAY.  The size of every dimension must have been
+     * already pushed on the stack.
+     *
+     * @param clazz            the array type.
+     * @param dim              the number of the dimensions.
+     * @return                 the value of <code>dim</code>.
+     */
+    public int addMultiNewarray(CtClass clazz, int dim) {
+       add(MULTIANEWARRAY);
+       addIndex(constPool.addClassInfo(clazz));
+       add(dim);
+       growStack(1 - dim);
+       return dim;
+    }
+
+    /**
+     * Appends MULTINEWARRAY.
+     *
+     * @param desc     the type descriptor of the created array.
+     * @param dim      dimensions.
+     * @return         the value of <code>dim</code>.
+     */
+    public int addMultiNewarray(String desc, int dim) {
+       add(MULTIANEWARRAY);
+       addIndex(constPool.addClassInfo(desc));
+       add(dim);
+       growStack(1 - dim);
+       return dim;
+    }
+
+    /**
+     * Appends PUTFIELD.
+     *
+     * @param c                the target class.
+     * @param name     the field name.
+     * @param desc     the descriptor of the field type.
+     */
+    public void addPutfield(CtClass c, String name, String desc) {
+       add(PUTFIELD);
+       int ci = constPool.addClassInfo(c);
+       addIndex(constPool.addFieldrefInfo(ci, name, desc));
+       growStack(-1 - Descriptor.dataSize(desc));
+    }
+
+    /**
+     * Appends PUTSTATIC.
+     *
+     * @param c                the target class.
+     * @param name     the field name.
+     * @param desc     the descriptor of the field type.
+     */
+    public void addPutstatic(CtClass c, String name, String desc) {
+       add(PUTSTATIC);
+       int ci = constPool.addClassInfo(c);
+       addIndex(constPool.addFieldrefInfo(ci, name, desc));
+       growStack(-Descriptor.dataSize(desc));
+    }
+
+    /**
+     * Appends ARETURN, IRETURN, .., or RETURN.
+     *
+     * @param type     the return type.
+     */
+    public void addReturn(CtClass type) {
+       if (type == null)
+           addOpcode(RETURN);
+       else if (type.isPrimitive()) {
+           CtPrimitiveType ptype = (CtPrimitiveType)type;
+           addOpcode(ptype.getReturnOp());
+       }
+       else
+           addOpcode(ARETURN);
+    }
+
+    /**
+     * Appends RET.
+     *
+     * @param var      local variable
+     */
+    public void addRet(int var) {
+       if (var < 0x100) {
+           addOpcode(RET);
+           add(var);
+       }
+       else {
+           addOpcode(WIDE);
+           addOpcode(RET);
+           addIndex(var);
+       }
+    }
+
+    /**
+     * Appends instructions for executing
+     * <code>java.lang.System.println(<i>message</i>)</code>.
+     *
+     * @param message          printed message.
+     */
+    public void addPrintln(String message) {
+       addGetstatic("java.lang.System", "err", "Ljava/io/PrintStream;");
+       addLdc(message);
+       addInvokevirtual("java.io.PrintStream",
+                        "println", "(Ljava/lang/String;)V");
+    }
+}
diff --git a/src/main/javassist/bytecode/ClassFile.java b/src/main/javassist/bytecode/ClassFile.java
new file mode 100644 (file)
index 0000000..063caa5
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.LinkedList;
+import java.util.List;
+import javassist.CannotCompileException;
+
+/**
+ * <code>ClassFile</code> represents a Java <code>.class</code> file,
+ * which consists of a constant pool, methods, fields, and attributes.
+ *
+ * @see javassist.CtClass#getClassFile()
+ */
+public final class ClassFile {
+    ConstPool constPool;
+    int thisClass;
+    int accessFlags;
+    int superClass;
+    int[] interfaces;
+    LinkedList fields;
+    LinkedList methods;
+    LinkedList attributes;
+
+    String thisclassname;      // not JVM-internal name
+
+    /**
+     * Constructs a class file from a byte stream.
+     */
+    public ClassFile(DataInputStream in) throws IOException {
+       read(in);
+    }
+
+    /**
+     * Constructs a class file including no members.
+     *
+     * @param isInterface      true if this is an interface.
+     *                         false if this is a class.
+     * @param classname                a fully-qualified class name
+     * @param superclass       a fully-qualified super class name
+     */
+    public ClassFile(boolean isInterface,
+                    String classname, String superclass) {
+       constPool = new ConstPool(classname);
+       thisClass = constPool.getThisClassInfo();
+       if (isInterface)
+           accessFlags = AccessFlag.SUPER | AccessFlag.INTERFACE
+               | AccessFlag.ABSTRACT;
+       else
+           accessFlags = AccessFlag.SUPER;
+
+       initSuperclass(superclass);
+       interfaces = null;
+       fields = new LinkedList();
+       methods = new LinkedList();
+       thisclassname = classname;
+
+       attributes = new LinkedList();
+       attributes.add(new SourceFileAttribute(constPool,
+                                       getSourcefileName(thisclassname)));
+    }
+
+    private void initSuperclass(String superclass) {
+       if (superclass != null)
+           superClass = constPool.addClassInfo(superclass);
+       else
+           superClass = constPool.addClassInfo("java.lang.Object");
+    }
+
+    private static String getSourcefileName(String qname) {
+       int index = qname.lastIndexOf('.');
+       if (index >= 0)
+           qname = qname.substring(index + 1);
+
+       return qname + ".java";
+    }
+
+    /**
+     * Returns a constant pool table.
+     */
+    public ConstPool getConstPool() {
+       return constPool;
+    }
+
+    /**
+     * Returns true if this is an interface.
+     */
+    public boolean isInterface() {
+       return (accessFlags & AccessFlag.INTERFACE) != 0;
+    }
+
+    /**
+     * Returns true if this is a final class or interface.
+     */
+    public boolean isFinal() {
+       return (accessFlags & AccessFlag.FINAL) != 0;
+    }
+
+    /**
+     * Returns true if this is an abstract class or an interface.
+     */
+    public boolean isAbstract() {
+       return (accessFlags & AccessFlag.ABSTRACT) != 0;
+    }
+
+    /**
+     * Returns access flags.
+     *
+     * @see javassist.bytecode.AccessFlag
+     */
+    public int getAccessFlags() {
+       return accessFlags;
+    }
+
+    /**
+     * Changes access flags.
+     *
+     * @see javassist.bytecode.AccessFlag
+     */
+    public void setAccessFlags(int acc) {
+       accessFlags = acc | AccessFlag.SUPER;
+    }
+
+    /**
+     * Returns the class name.
+     */
+    public String getName() {
+       return thisclassname;
+    }
+
+    /**
+     * Sets the class name.  This method substitutes the new name
+     * for all occurrences of the old class name in the class file.
+     */
+    public void setName(String name) {
+       renameClass(thisclassname, name);
+    }
+
+    /**
+     * Returns the super class name.
+     */
+    public String getSuperclass() {
+       return constPool.getClassInfo(superClass);
+    }
+
+    /**
+     * Returns the index of the constant pool entry representing
+     * the super class.
+     */
+    public int getSuperclassId() {
+       return superClass;
+    }
+
+    /**
+     * Sets the super class.
+     *
+     * <p>Unless the old super class is
+     * <code>java.lang.Object</code>, this method substitutes the new name
+     * for all occurrences of the old class name in the class file.
+     * If the old super class is <code>java.lang.Object</code>,
+     * only the calls to a super constructor are modified.
+     */
+    public void setSuperclass(String superclass)
+       throws CannotCompileException
+    {
+       if (constPool.getClassInfo(superClass).equals("java.lang.Object")) {
+           if (superclass != null)
+               try {
+                   superClass = constPool.addClassInfo(superclass);
+                   setSuperclass2(superclass);
+               }
+               catch (BadBytecode e) {
+                   throw new CannotCompileException(e);
+               }
+       }
+       else {
+           if (superclass == null)
+               superclass = "java.lang.Object";
+
+           renameClass(constPool.getClassInfo(superClass), superclass);
+       }
+    }
+
+    /* If the original super class is java.lang.Object, a special
+     * treatment is needed.  Some occurrences of java.lang.Object
+     * in the class file should not be changed into the new super
+     * class name.  For example, the call of Vector.addElement(Object)
+     * should not be changed into the call of Vector.addElement(X),
+     * where X is the new super class.
+     */
+    private void setSuperclass2(String superclass) throws BadBytecode {
+       LinkedList list = methods;
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           minfo.setSuperclass(superclass);
+       }
+    }
+
+    /**
+     * Replaces all occurrences of a class name in the class file.
+     *
+     * <p>If class X is substituted for class Y in the class file,
+     * X and Y must have the same signature.  If Y provides a method
+     * m(), X must provide it even if X inherits m() from the super class.
+     * If this fact is not guaranteed, the bytecode verifier may cause
+     * an error.
+     *
+     * @param oldname          the replaced class name
+     * @param newname          the substituted class name
+     */
+    public final void renameClass(String oldname, String newname) {
+       LinkedList list;
+       int n;
+
+       if (oldname.equals(newname))
+           return;
+
+       if (oldname.equals(thisclassname))
+           thisclassname = newname;
+
+       oldname = Descriptor.toJvmName(oldname);
+       newname = Descriptor.toJvmName(newname);
+       constPool.renameClass(oldname, newname);
+
+       list = methods;
+       n = list.size();
+       for (int i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           String desc = minfo.getDescriptor();
+           minfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
+       }
+
+       list = fields;
+       n = list.size();
+       for (int i = 0; i < n; ++i) {
+           FieldInfo finfo = (FieldInfo)list.get(i);
+           String desc = finfo.getDescriptor();
+           finfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
+       }
+    }
+
+    /**
+     * Replaces all occurrences of several class names in the class file.
+     *
+     * @param classnames       specifies which class name is replaced
+     *                         with which new name.  Class names must
+     *                         be described with the JVM-internal
+     *                         representation like
+     *                         <code>java/lang/Object</code>.
+     *
+     * @see #renameClass(String,String)
+     */
+    public final void renameClass(Map classnames) {
+       String jvmNewThisName
+           = (String)classnames.get(Descriptor.toJvmName(thisclassname));
+       if (jvmNewThisName != null)
+           thisclassname = Descriptor.toJavaName(jvmNewThisName);
+
+       constPool.renameClass(classnames);
+
+       LinkedList list = methods;
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           String desc = minfo.getDescriptor();
+           minfo.setDescriptor(Descriptor.rename(desc, classnames));
+       }
+
+       list = fields;
+       n = list.size();
+       for (int i = 0; i < n; ++i) {
+           FieldInfo finfo = (FieldInfo)list.get(i);
+           String desc = finfo.getDescriptor();
+           finfo.setDescriptor(Descriptor.rename(desc, classnames));
+       }
+    }
+
+    /**
+     * Returns the names of the interfaces implemented by the class.
+     */
+    public String[] getInterfaces() {
+       if (interfaces == null)
+           return new String[0];
+       else {
+           int n = interfaces.length;
+           String[] list = new String[n];
+           for (int i = 0; i < n; ++i)
+               list[i] = constPool.getClassInfo(interfaces[i]);
+
+           return list;
+       }
+    }
+
+    /**
+     * Sets the interfaces.
+     *
+     * @param nameList         the names of the interfaces.
+     */
+    public void setInterfaces(String[] nameList) {
+       if (nameList != null) {
+           int n = nameList.length;
+           interfaces = new int[n];
+           for (int i = 0; i < n; ++i)
+               interfaces[i] = constPool.addClassInfo(nameList[i]);
+       }
+    }
+
+    /**
+     * Appends an interface to the
+     * interfaces implemented by the class.
+     */
+    public void addInterface(String name) {
+       int info = constPool.addClassInfo(name);
+       if (interfaces == null) {
+           interfaces = new int[1];
+           interfaces[0] = info;
+       }
+       else {
+           int n = interfaces.length;
+           int[] newarray = new int[n + 1];
+           System.arraycopy(interfaces, 0, newarray, 0, n);
+           newarray[n] = info;
+           interfaces = newarray;
+       }
+    }
+
+    /**
+     * Returns all the fields declared in the class.
+     *
+     * @return a list of <code>FieldInfo</code>.
+     * @see FieldInfo
+     */
+    public List getFields() { return fields; }
+
+    /**
+     * Appends a field to the class.
+     */
+    public void addField(FieldInfo finfo) {
+       fields.add(finfo);
+    }
+
+    /**
+     * Returns all the methods declared in the class.
+     *
+     * @return a list of <code>MethodInfo</code>.
+     * @see MethodInfo
+     */
+    public List getMethods() { return methods; }
+
+    /**
+     * Returns the method with the specified name.  If there are multiple
+     * methods with that name, this method returns one of them.
+     *
+     * @return null            if no such a method is found.
+     */
+    public MethodInfo getMethod(String name) {
+       LinkedList list = methods;
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           if (minfo.getName().equals(name))
+               return minfo;
+       }
+
+       return null;
+    }
+
+    /**
+     * Returns a static initializer (class initializer), or null if
+     * it does not exist.
+     */
+    public MethodInfo getStaticInitializer() {
+       return getMethod(MethodInfo.nameClinit);
+    }
+
+    /**
+     * Appends a method to the class.
+     */
+    public void addMethod(MethodInfo minfo) {
+       methods.add(minfo);
+    }
+
+    /**
+     * Returns all the attributes.
+     *
+     * @return a list of <code>AttributeInfo</code> objects.
+     * @see AttributeInfo
+     */
+    public List getAttributes() { return attributes; }
+
+    /**
+     * Returns the attribute with the specified name.
+     *
+     * @param name     attribute name
+     */
+    public AttributeInfo getAttribute(String name) {
+       LinkedList list = attributes;
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           AttributeInfo ai = (AttributeInfo)list.get(i);
+           if (ai.getName().equals(name))
+               return ai;
+       }
+
+       return null;
+    }
+
+    /**
+     * Appends an attribute.  If there is already an attribute with
+     * the same name, the new one substitutes for it.
+     */
+    public void addAttribute(AttributeInfo info) {
+       AttributeInfo.remove(attributes, info.getName());
+       attributes.add(info);
+    }
+
+    /**
+     * Returns the source file containing this class.
+     *
+     * @return null    if this information is not available.
+     */
+    public String getSourceFile() {
+       SourceFileAttribute sf
+           = (SourceFileAttribute)getAttribute(SourceFileAttribute.tag);
+       if (sf == null)
+           return null;
+       else
+           return sf.getFileName();
+    }
+
+    private void read(DataInputStream in) throws IOException {
+       int i, n;
+       int magic = in.readInt();
+       if (magic != 0xCAFEBABE)
+           throw new IOException("non class file");
+
+       int major = in.readUnsignedShort();
+       int minor = in.readUnsignedShort();
+       constPool = new ConstPool(in);
+       accessFlags = in.readUnsignedShort();
+       thisClass = in.readUnsignedShort();
+       constPool.setThisClassInfo(thisClass);
+       superClass = in.readUnsignedShort();
+       n = in.readUnsignedShort();
+       if (n == 0)
+           interfaces = null;
+       else {
+           interfaces = new int[n];
+           for (i = 0; i < n; ++i)
+               interfaces[i] = in.readUnsignedShort();
+       }
+
+       ConstPool cp = constPool;
+       n = in.readUnsignedShort();
+       fields = new LinkedList();
+       for (i = 0; i < n; ++i)
+           addField(new FieldInfo(cp, in));
+
+       n = in.readUnsignedShort();
+       methods = new LinkedList();
+       for (i = 0; i < n; ++i)
+           addMethod(new MethodInfo(cp, in));
+
+       attributes = new LinkedList();
+       n = in.readUnsignedShort();
+       for (i = 0; i < n; ++i)
+           addAttribute(AttributeInfo.read(cp, in));
+
+       thisclassname = constPool.getClassInfo(thisClass);
+    }
+
+    /**
+     * Writes a class file represened by this object
+     * into an output stream.
+     */
+    public void write(DataOutputStream out) throws IOException {
+       int i, n;
+
+       out.writeInt(0xCAFEBABE);       // magic
+       out.writeShort(3);              // major version
+       out.writeShort(45);             // minor version
+       constPool.write(out);           // constant pool
+       out.writeShort(accessFlags);
+       out.writeShort(thisClass);
+       out.writeShort(superClass);
+
+       if (interfaces == null)
+           n = 0;
+       else
+           n = interfaces.length;
+
+       out.writeShort(n);
+       for (i = 0; i < n; ++i)
+           out.writeShort(interfaces[i]);
+
+       LinkedList list = fields;
+       n = list.size();
+       out.writeShort(n);
+       for (i = 0; i < n; ++i) {
+           FieldInfo finfo = (FieldInfo)list.get(i);
+           finfo.write(out);
+       }
+
+       list = methods;
+       n = list.size();
+       out.writeShort(n);
+       for (i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           minfo.write(out);
+       }
+
+       out.writeShort(attributes.size());
+       AttributeInfo.writeAll(attributes, out);
+    }
+}
diff --git a/src/main/javassist/bytecode/ClassFileWriter.java b/src/main/javassist/bytecode/ClassFileWriter.java
new file mode 100644 (file)
index 0000000..5b83a5a
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.PrintWriter;
+import javassist.Modifier;
+import java.util.List;
+
+/**
+ * A utility class for priting the contents of a class file.
+ * It prints a constant pool table, fields, and methods in a
+ * human readable representation.
+ */
+public class ClassFileWriter {
+    /**
+     * Prints the contents of a class file to the standard output stream.
+     */
+    public static void print(ClassFile cf) {
+       print(cf, new PrintWriter(System.out, true));
+    }
+
+    /**
+     * Prints the contents of a class file.
+     */
+    public static void print(ClassFile cf, PrintWriter out) {
+       List list;
+       int n;
+
+       /* 0x0020 (SYNCHRONIZED) means ACC_SUPER if the modifiers
+        * are of a class.
+        */
+       int mod
+           = AccessFlag.toModifier(cf.getAccessFlags()
+                                   & ~AccessFlag.SYNCHRONIZED);
+       out.println(Modifier.toString(mod) + " class "
+                   + cf.getName() + " extends " + cf.getSuperclass());
+       out.println();
+
+       ConstPool cp = cf.getConstPool();
+       list = cf.getFields();
+       n = list.size();
+       for (int i = 0; i < n; ++i) {
+           FieldInfo finfo = (FieldInfo)list.get(i);
+           int acc = finfo.getAccessFlags();
+           out.println(Modifier.toString(AccessFlag.toModifier(acc))
+                       + " " + finfo.getName() + "\t"
+                       + finfo.getDescriptor());
+           printAttributes(finfo.getAttributes(), out);
+       }
+
+       out.println();
+       list = cf.getMethods();
+       n = list.size();
+       for (int i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           int acc = minfo.getAccessFlags();
+           out.println(Modifier.toString(AccessFlag.toModifier(acc))
+                       + " " + minfo.getName() + "\t"
+                       + minfo.getDescriptor());
+           printAttributes(minfo.getAttributes(), out);
+           out.println();
+       }
+
+       out.println();
+       printAttributes(cf.getAttributes(), out);
+    }
+
+    static void printAttributes(List list, PrintWriter out) {
+       if (list == null)
+           return;
+
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           AttributeInfo ai = (AttributeInfo)list.get(i);
+           if (ai instanceof CodeAttribute) {
+               CodeAttribute ca = (CodeAttribute)ai;
+               out.println("attribute: " + ai.getName() + ": "
+                           + ai.getClass().getName());
+               out.println("max stack " + ca.getMaxStack()
+                           + ", max locals " + ca.getMaxLocals()
+                           + ", " + ca.getExceptionTable().size()
+                           + " catch blocks");
+               out.println("<code attribute begin>");
+               printAttributes(ca.getAttributes(), out);
+               out.println("<code attribute end>");
+           }
+           else
+               out.println("attribute: " + ai.getName()
+                           + " (" + ai.get().length + " byte): "
+                           + ai.getClass().getName());
+       }
+    }
+}
diff --git a/src/main/javassist/bytecode/CodeAttribute.java b/src/main/javassist/bytecode/CodeAttribute.java
new file mode 100644 (file)
index 0000000..6f45c03
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Map;
+import javassist.CtClass;
+
+/**
+ * <code>Code_attribute</code>.
+ *
+ * <p>To browse the <code>code</code> field of
+ * a <code>Code_attribute</code> structure,
+ * use <code>CodeIterator</code>.
+ *
+ * @see CodeIterator
+ */
+public class CodeAttribute extends AttributeInfo implements Opcode {
+    /**
+     * The name of this attribute <code>"Code"</code>.
+     */
+    public static final String tag = "Code";
+
+    // code[] is stored in AttributeInfo.info.
+
+    private int maxStack;
+    private int maxLocals;
+    private ExceptionTable exceptions;
+    private LinkedList attributes;
+
+    /**
+     * Constructs a <code>Code_attribute</code>.
+     *
+     * @param cp       constant pool table
+     * @param stack    <code>max_stack</code>
+     * @param locals   <code>max_locals</code>
+     * @param code     <code>code[]</code>
+     * @param etable   <code>exception_table[]</code>
+     */
+    public CodeAttribute(ConstPool cp, int stack, int locals, byte[] code,
+                        ExceptionTable etable)
+    {
+       super(cp, tag);
+       maxStack = stack;
+       maxLocals = locals;
+       info = code;
+       exceptions = etable;
+       attributes = new LinkedList();
+    }
+
+    /**
+     * Constructs a copy of <code>Code_attribute</code>.
+     * Specified class names are replaced during the copy.
+     *
+     * @param cp               constant pool table.
+     * @param src              source Code attribute.
+     * @param classnames       pairs of replaced and substituted
+     *                         class names.
+     */
+    private CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames)
+       throws BadBytecode
+    {
+       super(cp, tag);
+
+       maxStack = src.getMaxStack();
+       maxLocals = src.getMaxLocals();
+       exceptions = src.getExceptionTable().copy(cp, classnames);
+       info = src.copyCode(cp, classnames, exceptions);
+       attributes = new LinkedList();
+
+       /* Since an index into the source constant pool table may not
+          be translated, we don't copy the attributes.
+       */
+       /*
+       List src_attr = src.getAttributes();
+       int num = src_attr.size();
+       for (int i = 0; i < num; ++i) {
+           AttributeInfo ai = (AttributeInfo)src_attr.get(i);
+           attributes.add(ai.copy(cp, classnames));
+       }
+       */
+    }
+
+    CodeAttribute(ConstPool cp, int name_id, DataInputStream in)
+       throws IOException
+    {
+       super(cp, name_id, (byte[])null);
+       int attr_len = in.readInt();
+
+       maxStack = in.readUnsignedShort();
+       maxLocals = in.readUnsignedShort();
+
+       int code_len = in.readInt();
+       info = new byte[code_len];
+       in.readFully(info);
+
+       exceptions = new ExceptionTable(cp, in);
+
+       attributes = new LinkedList();
+       int num = in.readUnsignedShort();
+       for (int i = 0; i < num; ++i)
+           attributes.add(AttributeInfo.read(cp, in));
+    }
+
+    /**
+     * Makes a copy.  Class names are replaced according to the
+     * given <code>Map</code> object.
+     *
+     * @param newCp    the constant pool table used by the new copy.
+     * @param classnames       pairs of replaced and substituted
+     *                         class names.
+     * @exception RuntimeCopyException if a <code>BadBytecode</code>
+     *                         exception is thrown, it is
+     *                         converted into
+     *                         <code>RuntimeCopyException</code>.
+     *
+     * @return <code>CodeAttribute</code> object.
+     */
+    public AttributeInfo copy(ConstPool newCp, Map classnames)
+       throws RuntimeCopyException
+    {
+       try {
+           return new CodeAttribute(newCp, this, classnames);
+       }
+       catch (BadBytecode e) {
+           throw new RuntimeCopyException("bad bytecode. fatal?");
+       }
+    }
+
+    /**
+     * An exception that may be thrown by <code>copy()</code>
+     * in <code>CodeAttribute</code>.
+     */
+    public static class RuntimeCopyException extends RuntimeException {
+       /**
+        * Constructs an exception.
+        */
+       public RuntimeCopyException(String s) {
+           super(s);
+       }
+    }
+
+    /**
+     * Returns the length of this <code>attribute_info</code>
+     * structure.
+     * The returned value is <code>attribute_length + 6</code>.
+     */
+    public int length() {
+       return 18 + info.length + exceptions.size() * 8
+              + AttributeInfo.getLength(attributes);
+    }
+
+    void write(DataOutputStream out) throws IOException {
+       out.writeShort(name);           // attribute_name_index
+       out.writeInt(length() - 6);     // attribute_length
+       out.writeShort(maxStack);       // max_stack
+       out.writeShort(maxLocals);      // max_locals
+       out.writeInt(info.length);      // code_length
+       out.write(info);                // code
+       exceptions.write(out);
+       out.writeShort(attributes.size());      // attributes_count
+       AttributeInfo.writeAll(attributes, out);        // attributes
+    }
+
+    /**
+     * This method is not available.
+     *
+     * @throws java.lang.UnsupportedOperationException always thrown.
+     */
+    public byte[] get() { 
+       throw new UnsupportedOperationException("CodeAttribute.get()");
+    }
+
+    /**
+     * This method is not available.
+     *
+     * @throws java.lang.UnsupportedOperationException always thrown.
+     */
+    public void set(byte[] newinfo) {
+       throw new UnsupportedOperationException("CodeAttribute.set()");
+    }
+
+    /**
+     * Returns the name of the class declaring the method including
+     * this code attribute.
+     */
+    public String getDeclaringClass() {
+       ConstPool cp = getConstPool();
+       return cp.getClassName();
+    }
+
+    /**
+     * Returns <code>max_stack</code>.
+     */
+    public int getMaxStack() {
+       return maxStack;
+    }
+
+    /**
+     * Sets <code>max_stack</code>.
+     */
+    public void setMaxStack(int value) {
+       maxStack = value;
+    }
+
+    /**
+     * Returns <code>max_locals</code>.
+     */
+    public int getMaxLocals() {
+       return maxLocals;
+    }
+
+    /**
+     * Sets <code>max_locals</code>.
+     */
+    public void setMaxLocals(int value) {
+       maxLocals = value;
+    }
+
+    /**
+     * Returns <code>code_length</code>.
+     */
+    public int getCodeLength() {
+       return info.length;
+    }
+
+    /**
+     * Returns <code>code[]</code>.
+     */
+    public byte[] getCode() {
+       return info;
+    }
+
+    /**
+     * Sets <code>code[]</code>.
+     */
+    void setCode(byte[] newinfo) { super.set(newinfo); }
+
+    /**
+     * Makes a new iterator for reading this code attribute.
+     */
+    public CodeIterator iterator() {
+       return new CodeIterator(this);
+    }
+
+    /**
+     * Returns <code>exception_table[]</code>.
+     */
+    public ExceptionTable getExceptionTable() { return exceptions; }
+
+    /**
+     * Returns <code>attributes[]</code>.
+     * It returns a list of <code>AttributeInfo</code>.
+     *
+     * @see AttributeInfo
+     */
+    public List getAttributes() { return attributes; }
+
+    /**
+     * Returns the attribute with the specified name.
+     * If it is not found, this method returns null.
+     *
+     * @param name     attribute name
+     * @return         an <code>AttributeInfo</code> object or null.
+     */
+    public AttributeInfo getAttribute(String name) {
+       return AttributeInfo.lookup(attributes, name);
+    }
+
+    /**
+     * Copies code.
+     */
+    private byte[] copyCode(ConstPool destCp, Map classnames,
+                           ExceptionTable etable)
+       throws BadBytecode
+    {
+       int len = getCodeLength();
+       byte[] newCode = new byte[len];
+
+       LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(),
+                               newCode, destCp, classnames);
+       return LdcEntry.doit(newCode, ldc, etable);
+    }
+
+    private static LdcEntry copyCode(byte[] code, int beginPos, int endPos,
+                                    ConstPool srcCp, byte[] newcode,
+                                    ConstPool destCp, Map classnameMap)
+       throws BadBytecode
+    {
+       int i2, index;
+       LdcEntry ldcEntry = null;
+
+       for (int i = beginPos; i < endPos; i = i2) {
+           i2 = CodeIterator.nextOpcode(code, i);
+           byte c = code[i];
+           newcode[i] = c;
+           switch (c & 0xff) {
+           case LDC_W :
+           case LDC2_W :
+           case GETSTATIC :
+           case PUTSTATIC :
+           case GETFIELD :
+           case PUTFIELD :
+           case INVOKEVIRTUAL :
+           case INVOKESPECIAL :
+           case INVOKESTATIC :
+           case NEW :
+           case ANEWARRAY :
+           case CHECKCAST :
+           case INSTANCEOF :
+               copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
+                                 classnameMap);
+               break;
+           case LDC :
+               index = code[i + 1] & 0xff;
+               index = srcCp.copy(index, destCp, classnameMap);
+               if (index < 0x100)
+                   newcode[i + 1] = (byte)index;
+               else {
+                   LdcEntry ldc = new LdcEntry();
+                   ldc.where = i;
+                   ldc.index = index;
+                   ldc.next = ldcEntry;
+                   ldcEntry = ldc;
+               }
+               break;
+           case INVOKEINTERFACE :
+               copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
+                                 classnameMap);
+               newcode[i + 3] = code[i + 3];
+               newcode[i + 4] = code[i + 4];
+               break;
+           case MULTIANEWARRAY :
+               copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
+                                 classnameMap);
+               newcode[i + 3] = code[i + 3];
+               break;
+           default :
+               while (++i < i2)
+                   newcode[i] = code[i];
+
+               break;
+           }
+       }
+
+       return ldcEntry;
+    }
+
+    private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp,
+                                         byte[] newcode, ConstPool destCp,
+                                         Map classnameMap) {
+       int index = ((code[i] & 0xff) << 8) | (code[i + 1] & 0xff);
+       index = srcCp.copy(index, destCp, classnameMap);
+       newcode[i] = (byte)(index >> 8);
+       newcode[i + 1] = (byte)index;
+    }
+}
+
+final class LdcEntry {
+    LdcEntry next;
+    int where;
+    int index;
+
+    static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable)
+       throws BadBytecode
+    {
+       while (ldc != null) {
+           int where = ldc.where;
+           code = CodeIterator.insertGap(code, where, 1, false, etable);
+           code[where] = (byte)Opcode.LDC_W;
+           ByteArray.write16bit(ldc.index, code, where + 1);
+           ldc = ldc.next;
+       }
+
+       return code;
+    }
+}
diff --git a/src/main/javassist/bytecode/CodeIterator.java b/src/main/javassist/bytecode/CodeIterator.java
new file mode 100644 (file)
index 0000000..09735e2
--- /dev/null
@@ -0,0 +1,743 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+/**
+ * An iterator for editing a code attribute.
+ *
+ * <p>This iterator does not provide <code>remove()</code>.
+ * If a piece of code in a <code>Code_attribute</code> is unnecessary,
+ * it should be overwritten with <code>NOP</code>.
+ *
+ * @see CodeAttribute#iterator()
+ */
+public class CodeIterator implements Opcode {
+    protected CodeAttribute codeAttr;
+    protected byte[] bytecode;
+    protected int endPos;
+    protected int currentPos;
+
+    CodeIterator(CodeAttribute ca) {
+       codeAttr = ca;
+       bytecode = ca.getCode();
+       begin();
+    }
+
+    /**
+     * Moves to the first instruction.
+     */
+    public void begin() {
+       currentPos = 0;
+       endPos = getCodeLength();
+    }
+
+    /**
+     * Moves to the given index.
+     *
+     * <p>The index of the next instruction is set to the given index.
+     * The successive call to <code>next()</code>
+     * returns the index that has been given to <code>move()</code>.
+     *
+     * <p>Note that the index is into the byte array returned by
+     * <code>get().getCode()</code>.
+     *
+     * @see CodeAttribute#getCode()
+     */
+    public void move(int index) {
+       currentPos = index;
+    }
+
+    /**
+     * Returns a Code attribute read with this iterator.
+     */
+    public CodeAttribute get() {
+       return codeAttr;
+    }
+
+    /**
+     * Returns <code>code_length</code> of <code>Code_attribute</code>.
+     */
+    public int getCodeLength() {
+       return bytecode.length;
+    }
+
+    /**
+     * Returns the unsigned 8bit value at the given index.
+     */
+    public int byteAt(int index) { return bytecode[index] & 0xff; }
+
+    /**
+     * Writes an 8bit value at the given index.
+     */
+    public void writeByte(int value, int index) {
+       bytecode[index] = (byte)value;
+    }
+
+    /**
+     * Returns the unsigned 16bit value at the given index.
+     */
+    public int u16bitAt(int index) {
+       return ByteArray.readU16bit(bytecode, index);
+    }
+
+    /**
+     * Returns the signed 16bit value at the given index.
+     */
+    public int s16bitAt(int index) {
+       return ByteArray.readS16bit(bytecode, index);
+    }
+
+    /**
+     * Writes a 16 bit integer at the index.
+     */
+    public void write16bit(int value, int index) {
+       ByteArray.write16bit(value, bytecode, index);
+    }
+
+    /**
+     * Returns the signed 32bit value at the given index.
+     */
+    public int s32bitAt(int index) {
+       return ByteArray.read32bit(bytecode, index);
+    }
+
+    /**
+     * Writes a 32bit integer at the index.
+     */
+    public void write32bit(int value, int index) {
+       ByteArray.write32bit(value, bytecode, index);
+    }
+
+    /**
+     * Writes a byte array at the index.
+     */
+    public void write(byte[] code, int index) {
+       int len = code.length;
+       for (int j = 0; j < len; ++j)
+           bytecode[index++] = code[j];
+    }
+
+    /**
+     * Returns true if there is more instructions.
+     */
+    public boolean hasNext() { return currentPos < endPos; }
+
+    /**
+     * Returns the index of the next instruction
+     * (not the next opcode).
+     *
+     * <p>Note that the index is into the byte array returned by
+     * <code>get().getCode()</code>.
+     *
+     * @see CodeAttribute#getCode()
+     * @see CodeIterator#byteAt(int)
+     */
+    public int next() throws BadBytecode {
+       int pos = currentPos;
+       currentPos = nextOpcode(bytecode, pos);
+       return pos;
+    }
+
+    /**
+     * Moves to the first instruction following
+     * constructor invocation <code>super()</code> or <code>this()</code>.
+     *
+     * <p>This method skips all the instructions for executing
+     * <code>super()</code> or <code>this()</code>, which should be
+     * placed at the beginning of a constructor body.
+     *
+     * <p>This method returns the index of INVOKESPECIAL instruction
+     * executing <code>super()</code> or <code>this()</code>.
+     * A successive call to <code>next()</code> returns the
+     * index of the next instruction following that INVOKESPECIAL.
+     *
+     * <p>This method works only for a constructor.
+     *
+     * @return the index of the INVOKESPECIAL instruction, or -1
+     *         if a constructor invocation is not found.
+     */
+    public int skipConstructor() throws BadBytecode {
+       return skipSuperConstructor0(-1);
+    }
+
+    /**
+     * Moves to the first instruction following super
+     * constructor invocation <code>super()</code>.
+     *
+     * <p>This method skips all the instructions for executing
+     * <code>super()</code>, which should be
+     * placed at the beginning of a constructor body.
+     *
+     * <p>This method returns the index of INVOKESPECIAL instruction
+     * executing <code>super()</code>.
+     * A successive call to <code>next()</code> returns the
+     * index of the next instruction following that INVOKESPECIAL.
+     *
+     * <p>This method works only for a constructor.
+     *
+     * @return the index of the INVOKESPECIAL instruction, or -1
+     *         if a super constructor invocation is not found
+     *         but <code>this()</code> is found.
+     */
+    public int skipSuperConstructor() throws BadBytecode {
+       return skipSuperConstructor0(0);
+    }
+
+    /**
+     * Moves to the first instruction following explicit
+     * constructor invocation <code>this()</code>.
+     *
+     * <p>This method skips all the instructions for executing
+     * <code>this()</code>, which should be
+     * placed at the beginning of a constructor body.
+     *
+     * <p>This method returns the index of INVOKESPECIAL instruction
+     * executing <code>this()</code>.
+     * A successive call to <code>next()</code> returns the
+     * index of the next instruction following that INVOKESPECIAL.
+     *
+     * <p>This method works only for a constructor.
+     *
+     * @return the index of the INVOKESPECIAL instruction, or -1
+     *         if a explicit constructor invocation is not found
+     *         but <code>super()</code> is found.
+     */
+    public int skipThisConstructor() throws BadBytecode {
+       return skipSuperConstructor0(1);
+    }
+
+    /* skipSuper       1: this(), 0: super(), -1: both.
+     */
+    private int skipSuperConstructor0(int skipThis) throws BadBytecode {
+       begin();
+       ConstPool cp = codeAttr.getConstPool();
+       String thisClassName = codeAttr.getDeclaringClass();
+       int nested = 0;
+       while (hasNext()) {
+           int index = next();
+           int c = byteAt(index);
+           if (c == NEW)
+               ++nested;
+           else if (c == INVOKESPECIAL) {
+               int mref = ByteArray.readU16bit(bytecode, index + 1);
+               if (cp.getMethodrefName(mref).equals(MethodInfo.nameInit))
+                   if (--nested < 0) {
+                       if (skipThis < 0)
+                           return index;
+
+                       String cname = cp.getMethodrefClassName(mref);
+                       if (cname.equals(thisClassName) == (skipThis > 0))
+                           return index;
+                       else
+                           break;
+                   }
+           }
+       }
+
+       begin();
+       return -1;
+    }
+
+    /**
+     * Inserts the given bytecode sequence
+     * before the next instruction that would be returned by
+     * <code>next()</code> (not before the instruction returned
+     * by tha last call to <code>next()</code>).
+     * Branch offsets and the exception table are also updated.
+     *
+     * <p>If the next instruction is at the beginning of a block statement,
+     * then the bytecode is inserted within that block.
+     *
+     * <p>An extra gap may be inserted at the end of the inserted
+     * bytecode sequence for adjusting alignment if the code attribute
+     * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
+     *
+     * @param code     inserted bytecode sequence.
+     * @return         the index indicating the first byte of the
+     *                 inserted byte sequence.
+     */
+    public int insert(byte[] code)
+       throws BadBytecode
+    {
+       int pos = currentPos;
+       insert0(currentPos, code, false);
+       return pos;
+    }
+
+    /**
+     * Inserts the given bytecode sequence
+     * before the instruction at the given index <code>pos</code>.
+     * Branch offsets and the exception table are also updated.
+     *
+     * <p>If the instruction at the given index is at the beginning
+     * of a block statement,
+     * then the bytecode is inserted within that block.
+     *
+     * <p>An extra gap may be inserted at the end of the inserted
+     * bytecode sequence for adjusting alignment if the code attribute
+     * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
+     *
+     * @param pos      the index at which a byte sequence is inserted.
+     * @param code     inserted bytecode sequence.
+     */
+    public void insert(int pos, byte[] code) throws BadBytecode {
+       insert0(pos, code, false);
+    }
+
+    /**
+     * Inserts the given bytecode sequence exclusively
+     * before the next instruction that would be returned by
+     * <code>next()</code> (not before the instruction returned
+     * by tha last call to <code>next()</code>).
+     * Branch offsets and the exception table are also updated.
+     *
+     * <p>If the next instruction is at the beginning of a block statement,
+     * then the bytecode is excluded from that block.
+     *
+     * <p>An extra gap may be inserted at the end of the inserted
+     * bytecode sequence for adjusting alignment if the code attribute
+     * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
+     *
+     * @param code     inserted bytecode sequence.
+     * @return         the index indicating the first byte of the
+     *                 inserted byte sequence.
+     */
+    public int insertEx(byte[] code)
+       throws BadBytecode
+    {
+       int pos = currentPos;
+       insert0(currentPos, code, true);
+       return pos;
+    }
+
+    /**
+     * Inserts the given bytecode sequence exclusively
+     * before the instruction at the given index <code>pos</code>.
+     * Branch offsets and the exception table are also updated.
+     *
+     * <p>If the instruction at the given index is at the beginning
+     * of a block statement,
+     * then the bytecode is excluded from that block.
+     *
+     * <p>An extra gap may be inserted at the end of the inserted
+     * bytecode sequence for adjusting alignment if the code attribute
+     * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
+     *
+     * @param pos      the index at which a byte sequence is inserted.
+     * @param code     inserted bytecode sequence.
+     */
+    public void insertEx(int pos, byte[] code) throws BadBytecode {
+       insert0(pos, code, true);
+    }
+
+    private void insert0(int pos, byte[] code, boolean exclusive)
+       throws BadBytecode
+    {
+       int len = code.length;
+       if (len <= 0)
+           return;
+
+       insertGapCore(pos, len, exclusive);     // currentPos will change.
+       for (int j = 0; j < len; ++j)
+           bytecode[pos++] = code[j];
+    }
+
+    /**
+     * Inserts a gap
+     * before the next instruction that would be returned by
+     * <code>next()</code> (not before the instruction returned
+     * by tha last call to <code>next()</code>).
+     * Branch offsets and the exception table are also updated.
+     * The inserted gap is filled with NOP.  The gap length may be
+     * extended to a multiple of 4.
+     *
+     * <p>If the next instruction is at the beginning of a block statement,
+     * then the gap is inserted within that block.
+     *
+     * @param length           gap length
+     * @return the index indicating the first byte of the inserted gap.
+     */
+    public int insertGap(int length) throws BadBytecode {
+       int pos = currentPos;
+       insertGapCore(currentPos, length, false);
+       return pos;
+    }
+
+    /**
+     * Inserts a gap in front of the instruction at the given
+     * index <code>pos</code>.
+     * Branch offsets and the exception table are also updated.
+     * The inserted gap is filled with NOP.  The gap length may be
+     * extended to a multiple of 4.
+     *
+     * <p>If the instruction at the given index is at the beginning
+     * of a block statement,
+     * then the gap is inserted within that block.
+     *
+     * @param pos              the index at which a gap is inserted.
+     * @param length           gap length.
+     */
+    public void insertGap(int pos, int length) throws BadBytecode {
+       insertGapCore(pos, length, false);
+    }
+
+    /**
+     * Inserts an exclusive gap
+     * before the next instruction that would be returned by
+     * <code>next()</code> (not before the instruction returned
+     * by tha last call to <code>next()</code>).
+     * Branch offsets and the exception table are also updated.
+     * The inserted gap is filled with NOP.  The gap length may be
+     * extended to a multiple of 4.
+     *
+     * <p>If the next instruction is at the beginning of a block statement,
+     * then the gap is excluded from that block.
+     *
+     * @param length           gap length
+     * @return the index indicating the first byte of the inserted gap.
+     */
+    public int insertExGap(int length) throws BadBytecode {
+       int pos = currentPos;
+       insertGapCore(currentPos, length, true);
+       return pos;
+    }
+
+    /**
+     * Inserts an exclusive gap in front of the instruction at the given
+     * index <code>pos</code>.
+     * Branch offsets and the exception table are also updated.
+     * The inserted gap is filled with NOP.  The gap length may be
+     * extended to a multiple of 4.
+     *
+     * <p>If the instruction at the given index is at the beginning
+     * of a block statement,
+     * then the gap is excluded from that block.
+     *
+     * @param pos              the index at which a gap is inserted.
+     * @param length           gap length.
+     */
+    public void insertExGap(int pos, int length) throws BadBytecode {
+       insertGapCore(pos, length, true);
+    }
+
+    private void insertGapCore(int pos, int length, boolean exclusive)
+       throws BadBytecode
+    {
+       if (length <= 0)
+           return;
+
+       int cur = currentPos;
+       byte[] c = insertGap(bytecode, pos, length, exclusive,
+                            get().getExceptionTable());
+       if (cur >= pos)
+           currentPos = cur + (c.length - bytecode.length);
+
+       codeAttr.setCode(c);
+       bytecode = c;
+       endPos = getCodeLength();
+    }
+
+    /**
+     * Copies and inserts the entries in the given exception table
+     * at the beginning of the exception table in the code attribute
+     * edited by this object.
+     *
+     * @param offset   the value added to the code positions included
+     *                         in the entries.
+     */
+    public void insert(ExceptionTable et, int offset) {
+       codeAttr.getExceptionTable().add(0, et, offset);
+    }
+
+    /**
+     * Appends the given bytecode sequence at the end.
+     *
+     * @param code     the bytecode appended.
+     * @return the position of the first byte of the appended bytecode.
+     */
+    public int append(byte[] code) {
+       int size = getCodeLength();
+       int len = code.length;
+       if (len <= 0)
+           return size;
+
+       appendGap(len);
+       byte[] dest = bytecode;
+       for (int i = 0; i < len; ++i)
+           dest[i + size] = code[i];
+
+       return size;
+    }
+
+    /**
+     * Appends a gap at the end of the bytecode sequence.
+     *
+     * @param length           gap length
+     */
+    public void appendGap(int gapLength) {
+       byte[] code = bytecode;
+       int codeLength = code.length;
+       byte[] newcode = new byte[codeLength + gapLength];
+
+       int i;
+       for (i = 0; i < codeLength; ++i)
+           newcode[i] = code[i];
+
+       for (i = codeLength; i < codeLength + gapLength; ++i)
+           newcode[i] = NOP;
+
+       codeAttr.setCode(newcode);
+       bytecode = newcode;
+       endPos = getCodeLength();
+    }
+
+    /**
+     * Copies and appends the entries in the given exception table
+     * at the end of the exception table in the code attribute
+     * edited by this object.
+     *
+     * @param offset   the value added to the code positions included
+     *                         in the entries.
+     */
+    public void append(ExceptionTable et, int offset) {
+       ExceptionTable table = codeAttr.getExceptionTable();
+       table.add(table.size(), et, offset);
+    }
+
+    /* opcodeLegth is used for implementing nextOpcode().
+     */
+    private static final int opcodeLength[] = {
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3,
+       3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3,
+       3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3,
+       3, 3, 3, 3, 3, 5, 0, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3,
+       5, 5
+    };
+    // 0 .. UNUSED (186), LOOKUPSWITCH, TABLESWITCH, WIDE
+
+    /**
+     * Calculates the index of the next opcode.
+     */
+    static int nextOpcode(byte[] code, int index)
+       throws BadBytecode
+    {
+       int opcode;
+       try {
+           opcode = code[index] & 0xff;
+       }
+       catch (IndexOutOfBoundsException e) {
+           throw new BadBytecode("invalid opcode address");
+       }
+
+       try {
+           int len = opcodeLength[opcode];
+           if (len > 0)
+               return index + len;
+           else if (opcode == WIDE)
+               if (code[index + 1] == (byte)IINC)      // WIDE IINC
+                   return index + 6;
+               else
+                   return index + 4;           // WIDE ...
+           else {
+               int index2 = (index & ~3) + 8;
+               if (opcode == LOOKUPSWITCH) {
+                   int npairs = ByteArray.read32bit(code, index2);
+                   return index2 + npairs * 8 + 4;
+               }
+               else if (opcode == TABLESWITCH) {
+                   int low = ByteArray.read32bit(code, index2);
+                   int high = ByteArray.read32bit(code, index2 + 4);
+                   return index2 + (high - low + 1) * 4 + 8;
+               }
+               // else
+               //     throw new BadBytecode(opcode);
+           }
+       }
+       catch (IndexOutOfBoundsException e) {
+       }
+
+       // opcode is UNUSED or an IndexOutOfBoundsException was thrown.
+       throw new BadBytecode(opcode);
+    }
+
+    // methods for implementing insertGap().
+
+    /* If "where" is the beginning of a block statement, then the inserted
+     * gap is also included in the block statement.
+     * "where" must indicate the first byte of an opcode.
+     * The inserted gap is filled with NOP.  gapLength may be extended to
+     * a multiple of 4.
+     */
+    static byte[] insertGap(byte[] code, int where, int gapLength,
+                       boolean exclusive, ExceptionTable etable)
+       throws BadBytecode
+    {
+       if (gapLength <= 0)
+           return code;
+
+       try {
+           return insertGap0(code, where, gapLength, exclusive, etable);
+       }
+       catch (AlignmentException e) {
+           try {
+               return insertGap0(code, where, (gapLength + 3) & ~3,
+                                 exclusive, etable);
+           }
+           catch (AlignmentException e2) {
+               throw new RuntimeException("fatal error?");
+           }
+       }
+    }
+
+    private static byte[] insertGap0(byte[] code, int where, int gapLength,
+                               boolean exclusive, ExceptionTable etable)
+       throws BadBytecode, AlignmentException
+    {
+       int codeLength = code.length;
+       byte[] newcode = new byte[codeLength + gapLength];
+       insertGap2(code, where, gapLength, codeLength, newcode, exclusive);
+       etable.shiftPc(where, gapLength, exclusive);
+       return newcode;
+    }
+
+    private static void insertGap2(byte[] code, int where, int gapLength,
+                       int endPos, byte[] newcode, boolean exclusive)
+       throws BadBytecode, AlignmentException
+    {
+       int nextPos;
+       int i = 0;
+       int j = 0;
+       for (; i < endPos; i = nextPos) {
+           if (i == where) {
+               int j2 = j + gapLength;
+               while (j < j2)
+                   newcode[j++] = NOP;
+           }
+
+           nextPos = nextOpcode(code, i);
+           int inst = code[i] & 0xff;
+           // if<cond>, if_icmp<cond>, if_acmp<cond>, goto, jsr
+           if ((153 <= inst && inst <= 168)
+               || inst == IFNULL || inst == IFNONNULL) {
+               /* 2bytes *signed* offset */
+               int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff);
+               offset = newOffset(i, offset, where, gapLength, exclusive);
+               newcode[j] = code[i];
+               ByteArray.write16bit(offset, newcode, j + 1);
+               j += 3;
+           }
+           else if (inst == GOTO_W || inst == JSR_W) {
+               /* 4bytes offset */
+               int offset = ByteArray.read32bit(code, i + 1);
+               offset = newOffset(i, offset, where, gapLength, exclusive);
+               newcode[j++] = code[i];
+               ByteArray.write32bit(offset, newcode, j);
+               j += 4;
+           }
+           else if (inst == TABLESWITCH) {
+               if ((gapLength & 3) != 0)
+                   throw new AlignmentException();
+
+               int i0 = i;
+               int i2 = (i & ~3) + 4;  // 0-3 byte padding
+               while (i0 < i2)
+                   newcode[j++] = code[i0++];
+
+               int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2),
+                                           where, gapLength, exclusive);
+               ByteArray.write32bit(defaultbyte, newcode, j);
+               int lowbyte = ByteArray.read32bit(code, i2 + 4);
+               ByteArray.write32bit(lowbyte, newcode, j + 4);
+               int highbyte = ByteArray.read32bit(code, i2 + 8);
+               ByteArray.write32bit(highbyte, newcode, j + 8);
+               j += 12;
+               i0 = i2 + 12;
+               i2 = i0 + (highbyte - lowbyte + 1) * 4;
+               while (i0 < i2) {
+                   int offset = newOffset(i, ByteArray.read32bit(code, i0),
+                                          where, gapLength, exclusive);
+                   ByteArray.write32bit(offset, newcode, j);
+                   j += 4;
+                   i0 += 4;
+               }
+           }
+           else if (inst == LOOKUPSWITCH) {
+               if ((gapLength & 3) != 0)
+                   throw new AlignmentException();
+
+               int i0 = i;
+               int i2 = (i & ~3) + 4;  // 0-3 byte padding
+               while (i0 < i2)
+                   newcode[j++] = code[i0++];
+
+               int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2),
+                                           where, gapLength, exclusive);
+               ByteArray.write32bit(defaultbyte, newcode, j);
+               int npairs = ByteArray.read32bit(code, i2 + 4);
+               ByteArray.write32bit(npairs, newcode, j + 4);
+               j += 8;
+               i0 = i2 + 8;
+               i2 = i0 + npairs * 8;
+               while (i0 < i2) {
+                   ByteArray.copy32bit(code, i0, newcode, j);
+                   int offset = newOffset(i,
+                                       ByteArray.read32bit(code, i0 + 4),
+                                       where, gapLength, exclusive);
+                   ByteArray.write32bit(offset, newcode, j + 4);
+                   j += 8;
+                   i0 += 8;
+               }
+           }
+           else
+               while (i < nextPos)
+                   newcode[j++] = code[i++];
+           }
+    }
+
+    private static int newOffset(int i, int offset, int where,
+                                int gapLength, boolean exclusive) {
+       int target = i + offset;
+       if (i < where) {
+           if (where < target || (exclusive && where == target))
+               offset += gapLength;
+       }
+       else
+           if (target < where || (!exclusive && where == target))
+               offset -= gapLength;
+
+       return offset;
+    }
+}
+
+
+class AlignmentException extends Exception {
+}
diff --git a/src/main/javassist/bytecode/ConstPool.java b/src/main/javassist/bytecode/ConstPool.java
new file mode 100644 (file)
index 0000000..5d079e3
--- /dev/null
@@ -0,0 +1,1374 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Hashtable;
+import javassist.CtClass;
+
+/**
+ * Constant pool table.
+ */
+public final class ConstPool {
+    LongVector items;
+    int              numOfItems;
+    Hashtable classes;
+    Hashtable strings;
+    int              thisClassInfo;
+    private static final int SIZE = 128;
+
+    /**
+     * <code>CONSTANT_Class</code>
+     */
+    public static final int CONST_Class = ClassInfo.tag;
+
+    /**
+     * <code>CONSTANT_Fieldref</code>
+     */
+    public static final int CONST_Fieldref = FieldrefInfo.tag;
+
+    /**
+     * <code>CONSTANT_Methodref</code>
+     */
+    public static final int CONST_Methodref = MethodrefInfo.tag;
+
+    /**
+     * <code>CONSTANT_InterfaceMethodref</code>
+     */
+    public static final int CONST_InterfaceMethodref
+                                       = InterfaceMethodrefInfo.tag;
+
+    /**
+     * <code>CONSTANT_String</code>
+     */
+    public static final int CONST_String = StringInfo.tag;
+
+    /**
+     * <code>CONSTANT_Integer</code>
+     */
+    public static final int CONST_Integer = IntegerInfo.tag;
+
+    /**
+     * <code>CONSTANT_Float</code>
+     */
+    public static final int CONST_Float = IntegerInfo.tag;
+
+    /**
+     * <code>CONSTANT_Long</code>
+     */
+    public static final int CONST_Long = LongInfo.tag;
+
+    /**
+     * <code>CONSTANT_Double</code>
+     */
+    public static final int CONST_Double = DoubleInfo.tag;
+
+    /**
+     * <code>CONSTANT_NameAndType</code>
+     */
+    public static final int CONST_NameAndType = NameAndTypeInfo.tag;
+
+    /**
+     * <code>CONSTANT_Utf8</code>
+     */
+    public static final int CONST_Utf8 = Utf8Info.tag;
+
+    /**
+     * Represents the class using this constant pool table.
+     */
+    public static final CtClass THIS = null;
+
+    /**
+     * Constructs a constant pool table.
+     *
+     * @param thisclass                the name of the class using this constant
+     *                         pool table
+     */
+    public ConstPool(String thisclass) {
+       this();
+       thisClassInfo = addClassInfo(thisclass);
+    }
+
+    /**
+     * Constructs a constant pool table from the given byte stream.
+     *
+     * @param in       byte stream.
+     */
+    public ConstPool(DataInputStream in) throws IOException {
+       this();
+       read(in);
+    }
+
+    private ConstPool() {
+       items = new LongVector(SIZE);
+       numOfItems = 0;
+       addItem(null);          // index 0 is reserved by the JVM.
+       classes = new Hashtable();
+       strings = new Hashtable();
+       thisClassInfo = 0;
+    }
+
+    /**
+     * Returns the name of the class using this constant pool table.
+     */
+    public String getClassName() {
+       return getClassInfo(thisClassInfo);
+    }
+
+    /**
+     * Returns the index of <code>CONSTANT_Class_info</code> structure
+     * specifying the class using this constant pool table.
+     */
+    public int getThisClassInfo() {
+       return thisClassInfo;
+    }
+
+    void setThisClassInfo(int i) {
+       thisClassInfo = i;
+    }
+
+    ConstInfo getItem(int n) {
+       return (ConstInfo)items.elementAt(n);
+    }
+
+    /**
+     * Returns the <code>tag</code> field of the constant pool table
+     * entry at the given index.
+     */
+    public int getTag(int index) {
+       return getItem(index).getTag();
+    }
+
+    /**
+     * Reads <code>CONSTANT_Class_info</code> structure
+     * at the given index.
+     *
+     * @return a fully-qualified class or interface name specified
+     *         by <code>name_index</code>.
+     */
+    public String getClassInfo(int index) {
+       ClassInfo c = (ClassInfo)getItem(index);
+       if (c == null)
+           return null;
+       else
+           return Descriptor.toJavaName(getUtf8Info(c.name));
+    }
+
+    /**
+     * Reads the <code>name_index</code> field of the
+     * <code>CONSTANT_NameAndType_info</code> structure
+     * at the given index.
+     */
+    public int getNameAndTypeName(int index) {
+       NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index);
+       return ntinfo.memberName;
+    }
+
+    /**
+     * Reads the <code>descriptor_index</code> field of the
+     * <code>CONSTANT_NameAndType_info</code> structure
+     * at the given index.
+     */
+    public int getNameAndTypeDescriptor(int index) {
+       NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index);
+       return ntinfo.typeDescriptor;
+    }
+
+    /**
+     * Reads the <code>class_index</code> field of the
+     * <code>CONSTANT_Fieldref_info</code> structure
+     * at the given index.
+     */
+    public int getFieldrefClass(int index) {
+       FieldrefInfo finfo = (FieldrefInfo)getItem(index);
+       return finfo.classIndex;
+    }
+
+    /**
+     * Reads the <code>class_index</code> field of the
+     * <code>CONSTANT_Fieldref_info</code> structure
+     * at the given index.
+     *
+     * @return the name of the class at that <code>class_index</code>.
+     */
+    public String getFieldrefClassName(int index) {
+       FieldrefInfo f = (FieldrefInfo)getItem(index);
+       if (f == null)
+           return null;
+       else
+           return getClassInfo(f.classIndex);
+    }
+
+    /**
+     * Reads the <code>name_and_type_index</code> field of the
+     * <code>CONSTANT_Fieldref_info</code> structure
+     * at the given index.
+     */
+    public int getFieldrefNameAndType(int index) {
+       FieldrefInfo finfo = (FieldrefInfo)getItem(index);
+       return finfo.nameAndTypeIndex;
+    }
+
+    /**
+     * Reads the <code>name_index</code> field of the
+     * <code>CONSTANT_NameAndType_info</code> structure
+     * indirectly specified by the given index.
+     *
+     * @param index    an index to a <code>CONSTANT_Fieldref_info</code>.
+     * @return the name of the field.
+     */
+    public String getFieldrefName(int index) {
+       FieldrefInfo f = (FieldrefInfo)getItem(index);
+       if (f == null)
+           return null;
+       else {
+           NameAndTypeInfo n = (NameAndTypeInfo)getItem(f.nameAndTypeIndex);
+           if(n == null)
+               return null;
+           else
+               return getUtf8Info(n.memberName);
+       }
+    }
+
+    /**
+     * Reads the <code>descriptor_index</code> field of the
+     * <code>CONSTANT_NameAndType_info</code> structure
+     * indirectly specified by the given index.
+     *
+     * @param index    an index to a <code>CONSTANT_Fieldref_info</code>.
+     * @return the type descriptor of the field.
+     */
+    public String getFieldrefType(int index) {
+       FieldrefInfo f = (FieldrefInfo)getItem(index);
+       if (f == null)
+           return null;
+       else {
+           NameAndTypeInfo n = (NameAndTypeInfo) getItem(f.nameAndTypeIndex);
+           if(n == null)
+               return null;
+           else
+               return getUtf8Info(n.typeDescriptor);
+       }
+    }
+
+    /**
+     * Reads the <code>class_index</code> field of the
+     * <code>CONSTANT_Methodref_info</code> structure
+     * at the given index.
+     */
+    public int getMethodrefClass(int index) {
+       MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+       return minfo.classIndex;
+    }
+
+    /**
+     * Reads the <code>class_index</code> field of the
+     * <code>CONSTANT_Methodref_info</code> structure
+     * at the given index.
+     *
+     * @return the name of the class at that <code>class_index</code>.
+     */
+    public String getMethodrefClassName(int index) {
+       MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+       if (minfo == null)
+           return null;
+       else
+           return getClassInfo(minfo.classIndex);
+    }
+
+    /**
+     * Reads the <code>name_and_type_index</code> field of the
+     * <code>CONSTANT_Methodref_info</code> structure
+     * at the given index.
+     */
+    public int getMethodrefNameAndType(int index) {
+       MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+       return minfo.nameAndTypeIndex;
+    }
+
+    /**
+     * Reads the <code>name_index</code> field of the
+     * <code>CONSTANT_NameAndType_info</code> structure
+     * indirectly specified by the given index.
+     *
+     * @param index    an index to a <code>CONSTANT_Methodref_info</code>.
+     * @return the name of the method.
+     */
+    public String getMethodrefName(int index) {
+       MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+       if (minfo == null)
+           return null;
+       else {
+           NameAndTypeInfo n
+               = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
+           if(n == null)
+               return null;
+           else
+               return getUtf8Info(n.memberName);
+       }
+    }
+
+    /**
+     * Reads the <code>descriptor_index</code> field of the
+     * <code>CONSTANT_NameAndType_info</code> structure
+     * indirectly specified by the given index.
+     *
+     * @param index    an index to a <code>CONSTANT_Methodref_info</code>.
+     * @return the descriptor of the method.
+     */
+    public String getMethodrefType(int index) {
+       MethodrefInfo minfo = (MethodrefInfo)getItem(index);
+       if (minfo == null)
+           return null;
+       else {
+           NameAndTypeInfo n
+               = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
+           if(n == null)
+               return null;
+           else
+               return getUtf8Info(n.typeDescriptor);
+       }
+    }
+
+    /**
+     * Reads the <code>class_index</code> field of the
+     * <code>CONSTANT_InterfaceMethodref_info</code> structure
+     * at the given index.
+     */
+    public int getInterfaceMethodrefClass(int index) {
+       InterfaceMethodrefInfo minfo
+           = (InterfaceMethodrefInfo)getItem(index);
+       return minfo.classIndex;
+    }
+
+    /**
+     * Reads the <code>class_index</code> field of the
+     * <code>CONSTANT_InterfaceMethodref_info</code> structure
+     * at the given index.
+     *
+     * @return the name of the class at that <code>class_index</code>.
+     */
+    public String getInterfaceMethodrefClassName(int index) {
+       InterfaceMethodrefInfo minfo
+           = (InterfaceMethodrefInfo)getItem(index);
+       return getClassInfo(minfo.classIndex);
+    }
+
+    /**
+     * Reads the <code>name_and_type_index</code> field of the
+     * <code>CONSTANT_InterfaceMethodref_info</code> structure
+     * at the given index.
+     */
+    public int getInterfaceMethodrefNameAndType(int index) {
+       InterfaceMethodrefInfo minfo
+           = (InterfaceMethodrefInfo)getItem(index);
+       return minfo.nameAndTypeIndex;
+    }
+
+    /**
+     * Reads the <code>name_index</code> field of the
+     * <code>CONSTANT_NameAndType_info</code> structure
+     * indirectly specified by the given index.
+     *
+     * @param index    an index to
+     *                 a <code>CONSTANT_InterfaceMethodref_info</code>.
+     * @return the name of the method.
+     */
+    public String getInterfaceMethodrefName(int index) {
+       InterfaceMethodrefInfo minfo
+           = (InterfaceMethodrefInfo)getItem(index);
+       if (minfo == null)
+           return null;
+       else {
+           NameAndTypeInfo n
+               = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
+           if(n == null)
+               return null;
+           else
+               return getUtf8Info(n.memberName);
+       }
+    }
+
+    /**
+     * Reads the <code>descriptor_index</code> field of the
+     * <code>CONSTANT_NameAndType_info</code> structure
+     * indirectly specified by the given index.
+     *
+     * @param index    an index to
+     *                 a <code>CONSTANT_InterfaceMethodref_info</code>.
+     * @return the descriptor of the method.
+     */
+    public String getInterfaceMethodrefType(int index) {
+       InterfaceMethodrefInfo minfo
+           = (InterfaceMethodrefInfo)getItem(index);
+       if (minfo == null)
+           return null;
+       else {
+           NameAndTypeInfo n
+               = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
+           if(n == null)
+               return null;
+           else
+               return getUtf8Info(n.typeDescriptor);
+       }
+    }
+    /**
+     * Reads <code>CONSTANT_Integer_info</code>, <code>_Float_info</code>,
+     * <code>_Long_info</code>, <code>_Double_info</code>, or
+     * <code>_String_info</code> structure.
+     * These are used with the LDC instruction.
+     *
+     * @return a <code>String</code> value or a wrapped primitive-type
+     * value.
+     */
+    public Object getLdcValue(int index) {
+       ConstInfo constInfo = this.getItem(index);
+       Object value = null;
+       if (constInfo instanceof StringInfo)
+           value = this.getStringInfo(index);
+       else if (constInfo instanceof FloatInfo)
+           value = new Float(getFloatInfo(index));
+       else if (constInfo instanceof IntegerInfo)
+           value = new Integer(getIntegerInfo(index));
+       else if (constInfo instanceof LongInfo)
+           value = new Long(getLongInfo(index));
+       else if (constInfo instanceof DoubleInfo)
+           value = new Double(getDoubleInfo(index));
+       else
+           value = null;
+
+       return value;
+    }
+
+    /**
+     * Reads <code>CONSTANT_Integer_info</code> structure
+     * at the given index.
+     *
+     * @return the value specified by this entry.
+     */
+    public int getIntegerInfo(int index) {
+       IntegerInfo i = (IntegerInfo)getItem(index);
+       return i.value;
+    }
+
+    /**
+     * Reads <code>CONSTANT_Float_info</code> structure
+     * at the given index.
+     *
+     * @return the value specified by this entry.
+     */
+    public float getFloatInfo(int index) {
+       FloatInfo i = (FloatInfo)getItem(index);
+       return i.value;
+    }
+
+    /**
+     * Reads <code>CONSTANT_Long_info</code> structure
+     * at the given index.
+     *
+     * @return the value specified by this entry.
+     */
+    public long getLongInfo(int index) {
+       LongInfo i = (LongInfo)getItem(index);
+       return i.value;
+    }
+
+    /**
+     * Reads <code>CONSTANT_Double_info</code> structure
+     * at the given index.
+     *
+     * @return the value specified by this entry.
+     */
+    public double getDoubleInfo(int index) {
+       DoubleInfo i = (DoubleInfo)getItem(index);
+       return i.value;
+    }
+
+    /**
+     * Reads <code>CONSTANT_String_info</code> structure
+     * at the given index.
+     *
+     * @return the string specified by <code>string_index</code>.
+     */
+    public String getStringInfo(int index) {
+       StringInfo si = (StringInfo)getItem(index);
+       return getUtf8Info(si.string);
+    }
+
+    /**
+     * Reads <code>CONSTANT_utf8_info</code> structure
+     * at the given index.
+     *
+     * @return the string specified by this entry.
+     */
+    public String getUtf8Info(int index) {
+       Utf8Info utf = (Utf8Info)getItem(index);
+       return utf.string;
+    }
+
+    /**
+     * Determines whether <code>CONSTANT_Methodref_info</code>
+     * structure at the given index represents the constructor
+     * of the given class.
+     *
+     * @return         the <code>descriptor_index</code> specifying
+     *                 the type descriptor of the that constructor.
+     *                 If it is not that constructor,
+     *                 <code>isConstructor()</code> returns 0.
+     */
+    public int isConstructor(String classname, int index) {
+       return isMember(classname, MethodInfo.nameInit, index);
+    }
+
+    /**
+     * Determines whether <code>CONSTANT_Methodref_info</code>,
+     * <code>CONSTANT_Fieldref_info</code>, or
+     * <code>CONSTANT_InterfaceMethodref_info</code> structure
+     * at the given index represents the member with the specified
+     * name and declaring class.
+     *
+     * @param classname                the class declaring the member
+     * @param membername       the member name
+     * @param index            the index into the constant pool table
+     *
+     * @return         the <code>descriptor_index</code> specifying
+     *                 the type descriptor of that member.
+     *                 If it is not that member,
+     *                 <code>isMember()</code> returns 0.
+     */
+    public int isMember(String classname, String membername, int index) {
+       MemberrefInfo minfo = (MemberrefInfo)getItem(index);
+       if (getClassInfo(minfo.classIndex).equals(classname)) {
+           NameAndTypeInfo ntinfo
+               = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex);
+           if (getUtf8Info(ntinfo.memberName).equals(membername))
+               return ntinfo.typeDescriptor;
+       }
+
+       return 0;       // false
+    }
+
+    private int addItem(ConstInfo info) {
+       items.addElement(info);
+       return numOfItems++;
+    }
+
+    /**
+     * Copies the n-th item in this ConstPool object into the destination
+     * ConstPool object.
+     * The class names that the item refers to are renamed according
+     * to the given map.
+     *
+     * @param n                        the <i>n</i>-th item
+     * @param dest             destination constant pool table
+     * @param classnames       the map or null.
+     */
+    public int copy(int n, ConstPool dest, Map classnames) {
+       if (n == 0)
+           return 0;
+
+       ConstInfo info = getItem(n);
+       return info.copy(this, dest, classnames);
+    }
+
+    int addConstInfoPadding() {
+       return addItem(new ConstInfoPadding());
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Class_info</code> structure.
+     *
+     * <p>This also adds a <code>CONSTANT_Utf8_info</code> structure
+     * for storing the class name.
+     *
+     * @return         the index of the added entry.
+     */
+    public int addClassInfo(CtClass c) {
+       if (c == THIS)
+           return thisClassInfo;
+       else if (!c.isArray())
+           return addClassInfo(c.getName());
+       else {
+           // an array type is recorded in the hashtable with
+           // the key "[L<classname>;" instead of "<classname>".
+           //
+           // note: toJvmName(toJvmName(c)) is equal to toJvmName(c).
+
+           return addClassInfo(Descriptor.toJvmName(c));
+       }
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Class_info</code> structure.
+     *
+     * <p>This also adds a <code>CONSTANT_Utf8_info</code> structure
+     * for storing the class name.
+     *
+     * @param qname    a fully-qualified class name
+     *                 (or the JVM-internal representation of that name).
+     * @return         the index of the added entry.
+     */
+    public int addClassInfo(String qname) {
+       ClassInfo info = (ClassInfo)classes.get(qname);
+       if (info != null)
+           return info.index;
+       else {
+           int utf8 = addUtf8Info(Descriptor.toJvmName(qname));
+           info = new ClassInfo(utf8, numOfItems);
+           classes.put(qname, info);
+           return addItem(info);
+       }
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_NameAndType_info</code> structure.
+     *
+     * <p>This also adds <code>CONSTANT_Utf8_info</code> structures.
+     *
+     * @param name     <code>name_index</code>
+     * @param type     <code>descriptor_index</code>
+     * @return         the index of the added entry.
+     */
+    public int addNameAndTypeInfo(String name, String type) {
+       return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type));
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_NameAndType_info</code> structure.
+     *
+     * @param name     <code>name_index</code>
+     * @param type     <code>descriptor_index</code>
+     * @return         the index of the added entry.
+     */
+    public int addNameAndTypeInfo(int name, int type) {
+       return addItem(new NameAndTypeInfo(name, type));
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Fieldref_info</code> structure.
+     *
+     * <p>This also adds a new <code>CONSTANT_NameAndType_info</code>
+     * structure.
+     *
+     * @param classInfo                <code>class_index</code>
+     * @param name             <code>name_index</code>
+     *                         of <code>CONSTANT_NameAndType_info</code>.
+     * @param type             <code>descriptor_index</code>
+     *                         of <code>CONSTANT_NameAndType_info</code>.
+     * @return         the index of the added entry.
+     */
+    public int addFieldrefInfo(int classInfo, String name, String type) {
+       int nt = addNameAndTypeInfo(name, type);
+       return addFieldrefInfo(classInfo, nt);
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Fieldref_info</code> structure.
+     *
+     * @param classInfo                <code>class_index</code>
+     * @param nameandtypeinfo  <code>name_and_type_index</code>.
+     * @return         the index of the added entry.
+     */
+    public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) {
+       return addItem(new FieldrefInfo(classInfo, nameAndTypeInfo));
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Methodref_info</code> structure.
+     *
+     * <p>This also adds a new <code>CONSTANT_NameAndType_info</code>
+     * structure.
+     *
+     * @param classInfo                <code>class_index</code>
+     * @param name             <code>name_index</code>
+     *                         of <code>CONSTANT_NameAndType_info</code>.
+     * @param type             <code>descriptor_index</code>
+     *                         of <code>CONSTANT_NameAndType_info</code>.
+     * @return         the index of the added entry.
+     */
+    public int addMethodrefInfo(int classInfo, String name, String type) {
+       int nt = addNameAndTypeInfo(name, type);
+       return addMethodrefInfo(classInfo, nt);
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Methodref_info</code> structure.
+     *
+     * @param classInfo                <code>class_index</code>
+     * @param nameandtypeinfo  <code>name_and_type_index</code>.
+     * @return         the index of the added entry.
+     */
+    public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) {
+       return addItem(new MethodrefInfo(classInfo, nameAndTypeInfo));
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_InterfaceMethodref_info</code>
+     * structure.
+     *
+     * <p>This also adds a new <code>CONSTANT_NameAndType_info</code>
+     * structure.
+     *
+     * @param classInfo                <code>class_index</code>
+     * @param name             <code>name_index</code>
+     *                         of <code>CONSTANT_NameAndType_info</code>.
+     * @param type             <code>descriptor_index</code>
+     *                         of <code>CONSTANT_NameAndType_info</code>.
+     * @return         the index of the added entry.
+     */
+    public int addInterfaceMethodrefInfo(int classInfo, String name,
+                                        String type) {
+       int nt = addNameAndTypeInfo(name, type);
+       return addInterfaceMethodrefInfo(classInfo, nt);
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_InterfaceMethodref_info</code>
+     * structure.
+     *
+     * @param classInfo                <code>class_index</code>
+     * @param nameandtypeinfo  <code>name_and_type_index</code>.
+     * @return         the index of the added entry.
+     */
+    public int addInterfaceMethodrefInfo(int classInfo,
+                                        int nameAndTypeInfo) {
+       return addItem(new InterfaceMethodrefInfo(classInfo,
+                                                 nameAndTypeInfo));
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_String_info</code>
+     * structure.
+     *
+     * <p>This also adds a new <code>CONSTANT_Utf8_info</code>
+     * structure.
+     *
+     * @return         the index of the added entry.
+     */
+    public int addStringInfo(String str) {
+       return addItem(new StringInfo(addUtf8Info(str)));
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Integer_info</code>
+     * structure.
+     *
+     * @return         the index of the added entry.
+     */
+    public int addIntegerInfo(int i) {
+       return addItem(new IntegerInfo(i));
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Float_info</code>
+     * structure.
+     *
+     * @return         the index of the added entry.
+     */
+    public int addFloatInfo(float f) {
+       return addItem(new FloatInfo(f));
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Long_info</code>
+     * structure.
+     *
+     * @return         the index of the added entry.
+     */
+    public int addLongInfo(long l) {
+       int i = addItem(new LongInfo(l));
+       addItem(new ConstInfoPadding());
+       return i;
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Double_info</code>
+     * structure.
+     *
+     * @return         the index of the added entry.
+     */
+    public int addDoubleInfo(double d) {
+       int i = addItem(new DoubleInfo(d));
+       addItem(new ConstInfoPadding());
+       return i;
+    }
+
+    /**
+     * Adds a new <code>CONSTANT_Utf8_info</code>
+     * structure.
+     *
+     * <p>If the given utf8 string has been already recorded in the
+     * table, then this method does not add a new entry to avoid adding
+     * a duplicated entry.
+     * Instead, it returns the index of the entry already recorded.
+     *
+     * @return         the index of the added entry.
+     */
+    public int addUtf8Info(String utf8) {
+       Utf8Info info = (Utf8Info)strings.get(utf8);
+       if (info != null)
+           return info.index;
+       else {
+           info = new Utf8Info(utf8, numOfItems);
+           strings.put(utf8, info);
+           return addItem(info);
+       }
+    }
+
+    /**
+     * Replaces all occurrences of a class name.
+     *
+     * @param oldName          the replaced name
+     * @param newName          the substituted name.
+     */
+    public void renameClass(String oldName, String newName) {
+       LongVector v = items;
+       int size = numOfItems;
+       for (int i = 1; i < size; ++i)
+           ((ConstInfo)v.elementAt(i)).renameClass(this, oldName, newName);
+    }
+
+    /**
+     * Replaces all occurrences of class names.
+     *
+     * @param classnames       specifies pairs of replaced and substituted
+     *                         name.
+     */
+    public void renameClass(Map classnames) {
+       LongVector v = items;
+       int size = numOfItems;
+       for (int i = 1; i < size; ++i)
+           ((ConstInfo)v.elementAt(i)).renameClass(this, classnames);
+    }
+
+    private void read(DataInputStream in) throws IOException {
+       int n = in.readUnsignedShort();
+       while (--n > 0) {       // index 0 is reserved by JVM
+           int tag = readOne(in);
+           if ((tag == LongInfo.tag) || (tag == DoubleInfo.tag)) {
+               addItem(new ConstInfoPadding());
+               --n;
+           }
+       }
+    }
+
+    private int readOne(DataInputStream in) throws IOException {
+       ConstInfo info;
+       int tag = in.readUnsignedByte();
+       switch (tag) {
+       case Utf8Info.tag :                     // 1
+           info = new Utf8Info(in, numOfItems);
+           strings.put(((Utf8Info)info).string, info);
+           break;
+       case IntegerInfo.tag :                  // 3
+           info = new IntegerInfo(in);
+           break;
+       case FloatInfo.tag :                    // 4
+           info = new FloatInfo(in);
+           break;
+       case LongInfo.tag :                     // 5
+           info = new LongInfo(in);
+           break;
+       case DoubleInfo.tag :                   // 6
+           info = new DoubleInfo(in);
+           break;
+       case ClassInfo.tag :                    // 7
+           info = new ClassInfo(in, numOfItems);
+           // classes.put(<classname>, info);
+           break;
+       case StringInfo.tag :                   // 8
+           info = new StringInfo(in);
+           break;
+       case FieldrefInfo.tag :                 // 9
+           info = new FieldrefInfo(in);
+           break;
+       case MethodrefInfo.tag :                // 10
+           info = new MethodrefInfo(in);
+           break;
+       case InterfaceMethodrefInfo.tag :       // 11
+           info = new InterfaceMethodrefInfo(in);
+           break;
+       case NameAndTypeInfo.tag :              // 12
+           info = new NameAndTypeInfo(in);
+           break;
+       default :
+           throw new IOException("invalid constant type: " + tag);
+       }
+
+       addItem(info);
+       return tag;
+    }
+
+    /**
+     * Writes the contents of the constant pool table.
+     */
+    public void write(DataOutputStream out) throws IOException {
+       out.writeShort(numOfItems);
+       LongVector v = items;
+       int size = numOfItems;
+       for (int i = 1; i < size; ++i)
+           ((ConstInfo)v.elementAt(i)).write(out);
+    }
+
+    /**
+     * Prints the contents of the constant pool table.
+     */
+    public void print() {
+       print(new PrintWriter(System.out, true));
+    }
+
+    /**
+     * Prints the contents of the constant pool table.
+     */
+    public void print(PrintWriter out) {
+       int size = numOfItems;
+       for (int i = 1; i < size; ++i) {
+           out.print(i);
+           out.print(" ");
+           ((ConstInfo)items.elementAt(i)).print(out);
+       }
+    }
+}
+
+abstract class ConstInfo {
+    public abstract int getTag();
+
+    public void renameClass(ConstPool cp, String oldName, String newName) {}
+    public void renameClass(ConstPool cp, Map classnames) {}
+    public abstract int copy(ConstPool src, ConstPool dest, Map classnames);
+                       // ** classnames is a mapping between JVM names.
+
+    public abstract void write(DataOutputStream out) throws IOException;
+    public abstract void print(PrintWriter out);
+
+    public String toString() {
+       ByteArrayOutputStream bout = new ByteArrayOutputStream();
+       PrintWriter out = new PrintWriter(bout);
+       print(out);
+       return bout.toString();
+    }
+}
+
+/* padding following DoubleInfo or LongInfo.
+ */
+class ConstInfoPadding extends ConstInfo {
+    public int getTag() { return 0; }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       return dest.addConstInfoPadding();
+    }
+
+    public void write(DataOutputStream out) throws IOException {}
+
+    public void print(PrintWriter out) {
+       out.println("padding");
+    }
+}
+
+class ClassInfo extends ConstInfo {
+    static final int tag = 7;
+    int name;
+    int index;
+
+    public ClassInfo(int className, int i) {
+       name = className;
+       index = i;
+    }
+
+    public ClassInfo(DataInputStream in, int i) throws IOException {
+       name = in.readUnsignedShort();
+       index = i;
+    }
+
+    public int getTag() { return tag; }
+
+    public void renameClass(ConstPool cp, String oldName, String newName) {
+       if (cp.getUtf8Info(name).equals(oldName))
+           name = cp.addUtf8Info(newName);
+    }
+
+    public void renameClass(ConstPool cp, Map map) {
+       String oldName = cp.getUtf8Info(name);
+       String newName = (String)map.get(oldName);
+       if (newName != null && !newName.equals(oldName))
+           name = cp.addUtf8Info(newName);
+    }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       String classname = src.getUtf8Info(name);
+       if (map != null) {
+           String newname = (String)map.get(classname);
+           if (newname != null)
+               classname = newname;
+       }
+
+       return dest.addClassInfo(classname);
+    }
+
+    public void write(DataOutputStream out) throws IOException {
+       out.writeByte(tag);
+       out.writeShort(name);
+    }
+
+    public void print(PrintWriter out) {
+       out.print("Class #");
+       out.println(name);
+    }
+}
+
+class NameAndTypeInfo extends ConstInfo {
+    static final int tag = 12;
+    int memberName;
+    int typeDescriptor;
+
+    public NameAndTypeInfo(int name, int type) {
+       memberName = name;
+       typeDescriptor = type;
+    }
+
+    public NameAndTypeInfo(DataInputStream in) throws IOException {
+       memberName = in.readUnsignedShort();
+       typeDescriptor = in.readUnsignedShort();
+    }
+
+    public int getTag() { return tag; }
+
+    public void renameClass(ConstPool cp, String oldName, String newName) {
+       String type = cp.getUtf8Info(typeDescriptor);
+       String type2 = Descriptor.rename(type, oldName, newName);
+       if (type != type2)
+           typeDescriptor = cp.addUtf8Info(type2);
+    }
+
+    public void renameClass(ConstPool cp, Map map) {
+       String type = cp.getUtf8Info(typeDescriptor);
+       String type2 = Descriptor.rename(type, map);
+       if (type != type2)
+           typeDescriptor = cp.addUtf8Info(type2);
+    }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       String mname = src.getUtf8Info(memberName);
+       String tdesc = src.getUtf8Info(typeDescriptor);
+       tdesc = Descriptor.rename(tdesc, map);
+       return dest.addNameAndTypeInfo(dest.addUtf8Info(mname),
+                                      dest.addUtf8Info(tdesc));
+    }
+
+    public void write(DataOutputStream out) throws IOException {
+       out.writeByte(tag);
+       out.writeShort(memberName);
+       out.writeShort(typeDescriptor);
+    }
+
+    public void print(PrintWriter out) {
+       out.print("NameAndType #");
+       out.print(memberName);
+       out.print(", type #");
+       out.println(typeDescriptor);
+    }
+}
+
+abstract class MemberrefInfo extends ConstInfo {
+    int classIndex;
+    int nameAndTypeIndex;
+
+    public MemberrefInfo(int cindex, int ntindex) {
+       classIndex = cindex;
+       nameAndTypeIndex = ntindex;
+    }
+
+    public MemberrefInfo(DataInputStream in) throws IOException {
+       classIndex = in.readUnsignedShort();
+       nameAndTypeIndex = in.readUnsignedShort();
+    }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       int classIndex2 = src.getItem(classIndex).copy(src, dest, map);
+       int ntIndex2 = src.getItem(nameAndTypeIndex).copy(src, dest, map);
+       return copy2(dest, classIndex2, ntIndex2);
+    }
+
+    abstract protected int copy2(ConstPool dest, int cindex, int ntindex);
+
+    public void write(DataOutputStream out) throws IOException {
+       out.writeByte(getTag());
+       out.writeShort(classIndex);
+       out.writeShort(nameAndTypeIndex);
+    }
+
+    public void print(PrintWriter out) {
+       out.print(getTagName() + " #");
+       out.print(classIndex);
+       out.print(", name&type #");
+       out.println(nameAndTypeIndex);
+    }
+
+    public abstract String getTagName();
+}
+
+class FieldrefInfo extends MemberrefInfo {
+    static final int tag = 9;
+
+    public FieldrefInfo(int cindex, int ntindex) {
+       super(cindex, ntindex);
+    }
+
+    public FieldrefInfo(DataInputStream in) throws IOException {
+       super(in);
+    }
+
+    public int getTag() { return tag; }
+
+    public String getTagName() { return "Field"; }
+
+    protected int copy2(ConstPool dest, int cindex, int ntindex) {
+       return dest.addFieldrefInfo(cindex, ntindex);
+    }
+}
+
+class MethodrefInfo extends MemberrefInfo {
+    static final int tag = 10;
+
+    public MethodrefInfo(int cindex, int ntindex) {
+       super(cindex, ntindex);
+    }
+
+    public MethodrefInfo(DataInputStream in) throws IOException {
+       super(in);
+    }
+
+    public int getTag() { return tag; }
+
+    public String getTagName() { return "Method"; }
+
+    protected int copy2(ConstPool dest, int cindex, int ntindex) {
+       return dest.addMethodrefInfo(cindex, ntindex);
+    }
+}
+
+class InterfaceMethodrefInfo extends MemberrefInfo {
+    static final int tag = 11;
+
+    public InterfaceMethodrefInfo(int cindex, int ntindex) {
+       super(cindex, ntindex);
+    }
+
+    public InterfaceMethodrefInfo(DataInputStream in) throws IOException {
+       super(in);
+    }
+
+    public int getTag() { return tag; }
+
+    public String getTagName() { return "Interface"; }
+
+    protected int copy2(ConstPool dest, int cindex, int ntindex) {
+       return dest.addInterfaceMethodrefInfo(cindex, ntindex);
+    }
+}
+
+class StringInfo extends ConstInfo {
+    static final int tag = 8;
+    int string;
+
+    public StringInfo(int str) {
+       string = str;
+    }
+
+    public StringInfo(DataInputStream in) throws IOException {
+       string = in.readUnsignedShort();
+    }
+
+    public int getTag() { return tag; }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       return dest.addStringInfo(src.getUtf8Info(string));
+    }
+
+    public void write(DataOutputStream out) throws IOException {
+       out.writeByte(tag);
+       out.writeShort(string);
+    }
+
+    public void print(PrintWriter out) {
+       out.print("String #");
+       out.println(string);
+    }
+}
+
+class IntegerInfo extends ConstInfo {
+    static final int tag = 3;
+    int value;
+
+    public IntegerInfo(int i) {
+       value = i;
+    }
+
+    public IntegerInfo(DataInputStream in) throws IOException {
+       value = in.readInt();
+    }
+
+    public int getTag() { return tag; }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       return dest.addIntegerInfo(value);
+    }
+
+    public void write(DataOutputStream out) throws IOException {
+       out.writeByte(tag);
+       out.writeInt(value);
+    }
+
+    public void print(PrintWriter out) {
+       out.print("Integer ");
+       out.println(value);
+    }
+}
+
+class FloatInfo extends ConstInfo {
+    static final int tag = 4;
+    float value;
+
+    public FloatInfo(float f) {
+       value = f;
+    }
+
+    public FloatInfo(DataInputStream in) throws IOException {
+       value = in.readFloat();
+    }
+
+    public int getTag() { return tag; }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       return dest.addFloatInfo(value);
+    }
+
+    public void write(DataOutputStream out) throws IOException {
+       out.writeByte(tag);
+       out.writeFloat(value);
+    }
+
+    public void print(PrintWriter out) {
+       out.print("Float ");
+       out.println(value);
+    }
+}
+
+class LongInfo extends ConstInfo {
+    static final int tag = 5;
+    long value;
+
+    public LongInfo(long l) {
+       value = l;
+    }
+
+    public LongInfo(DataInputStream in) throws IOException {
+       value = in.readLong();
+    }
+
+    public int getTag() { return tag; }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       return dest.addLongInfo(value);
+    }
+
+    public void write(DataOutputStream out) throws IOException {
+       out.writeByte(tag);
+       out.writeLong(value);
+    }
+
+    public void print(PrintWriter out) {
+       out.print("Long ");
+       out.println(value);
+    }
+}
+
+class DoubleInfo extends ConstInfo {
+    static final int tag = 6;
+    double value;
+
+    public DoubleInfo(double d) {
+       value = d;
+    }
+
+    public DoubleInfo(DataInputStream in) throws IOException {
+       value = in.readDouble();
+    }
+
+    public int getTag() { return tag; }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       return dest.addDoubleInfo(value);
+    }
+
+    public void write(DataOutputStream out) throws IOException {
+       out.writeByte(tag);
+       out.writeDouble(value);
+    }
+
+    public void print(PrintWriter out) {
+       out.print("Double ");
+       out.println(value);
+    }
+}
+
+class Utf8Info extends ConstInfo {
+    static final int tag = 1;
+    String string;
+    int index;
+
+    public Utf8Info(String utf8, int i) {
+       string = utf8;
+       index = i;
+    }
+
+    public Utf8Info(DataInputStream in, int i) throws IOException {
+       string = in.readUTF();
+       index = i;
+    }
+
+    public int getTag() { return tag; }
+
+    public int copy(ConstPool src, ConstPool dest, Map map) {
+       return dest.addUtf8Info(string);
+    }
+
+    public void write(DataOutputStream out) throws IOException {
+       out.writeByte(tag);
+       out.writeUTF(string);
+    }
+
+    public void print(PrintWriter out) {
+       out.print("UTF8 \"");
+       out.print(string);
+       out.println("\"");
+    }
+}
diff --git a/src/main/javassist/bytecode/ConstantAttribute.java b/src/main/javassist/bytecode/ConstantAttribute.java
new file mode 100644 (file)
index 0000000..4d913e1
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.util.Map;
+import java.io.IOException;
+
+/**
+ * <code>ConstantValue_attribute</code>.
+ */
+public class ConstantAttribute extends AttributeInfo {
+    /**
+     * The name of this attribute <code>"ConstantValue"</code>.
+     */
+    public static final String tag = "ConstantValue";
+
+    ConstantAttribute(ConstPool cp, int n, DataInputStream in)
+       throws IOException
+    {
+       super(cp, n, in);
+    }
+
+    /**
+     * Constructs a ConstantValue attribute.
+     *
+     * @param cp               a constant pool table.
+     * @param index            <code>constantvalue_index</code>
+     *                         of <code>ConstantValue_attribute</code>.
+     */
+    public ConstantAttribute(ConstPool cp, int index) {
+       super(cp, tag);
+       byte[] bvalue = new byte[2];
+       bvalue[0] = (byte)(index >>> 8);
+       bvalue[1] = (byte)index;
+       set(bvalue);
+    }
+
+    /**
+     * Returns <code>constantvalue_index</code>.
+     */
+    public int getConstantValue() {
+       return ByteArray.readU16bit(get(), 0);
+    }
+
+    /**
+     * Makes a copy.  Class names are replaced according to the
+     * given <code>Map</code> object.
+     *
+     * @param newCp    the constant pool table used by the new copy.
+     * @param classnames       pairs of replaced and substituted
+     *                         class names.
+     */
+    public AttributeInfo copy(ConstPool newCp, Map classnames) {
+       int index = getConstPool().copy(getConstantValue(), newCp,
+                                       classnames);
+       return new ConstantAttribute(newCp, index);
+    }
+}
diff --git a/src/main/javassist/bytecode/Descriptor.java b/src/main/javassist/bytecode/Descriptor.java
new file mode 100644 (file)
index 0000000..065fbcc
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.util.Map;
+import javassist.CtClass;
+import javassist.CtPrimitiveType;
+import javassist.ClassPool;
+import javassist.NotFoundException;
+
+/**
+ * A support class for dealing with descriptors.
+ *
+ * <p>See chapter 4.3 in "The Java Virtual Machine Specification (2nd ed.)"
+ */
+public class Descriptor {
+    /**
+     * Converts a class name into the internal representation used in
+     * the JVM.
+     *
+     * <p>Note that <code>toJvmName(toJvmName(s))</code> is equivalent
+     * to <code>toJvmName(s)</code>.
+     */
+    public static String toJvmName(String classname) {
+       return classname.replace('.', '/');
+    }
+
+    /**
+     * Converts a class name from the internal representation used in
+     * the JVM to the normal one used in Java.
+     */
+    public static String toJavaName(String classname) {
+       return classname.replace('/', '.');
+    }
+
+    /**
+     * Returns the internal representation of the class name in the
+     * JVM.
+     */
+    public static String toJvmName(CtClass clazz) {
+       if (clazz.isArray())
+           return of(clazz);
+       else
+           return toJvmName(clazz.getName());
+    }
+
+    /**
+     * Substitutes a class name
+     * in the given descriptor string.
+     *
+     * @param desc             descriptor string
+     * @param oldname          replaced JVM class name
+     * @param newname          substituted JVM class name
+     *
+     * @see Descriptor#toJvmName(String)
+     */
+    public static String rename(String desc,
+                               String oldname, String newname) {
+       if (desc.indexOf(oldname) < 0)
+           return desc;
+
+       StringBuffer newdesc = new StringBuffer();
+       int head = 0;
+       int i = 0;
+       for (;;) {
+           int j = desc.indexOf('L', i);
+           if (j < 0)
+               break;
+           else if (desc.startsWith(oldname, j + 1)
+                    && desc.charAt(j + oldname.length() + 1) == ';') {
+               newdesc.append(desc.substring(head, j));
+               newdesc.append('L');
+               newdesc.append(newname);
+               newdesc.append(';');
+               head = i = j + oldname.length() + 2;
+           }
+           else {
+               i = desc.indexOf(';', j) + 1;
+               if (i < 1)
+                   break;      // ';' was not found.
+           }
+       }
+
+       if (head == 0)
+           return desc;
+       else {
+           int len = desc.length();
+           if (head < len)
+               newdesc.append(desc.substring(head, len));
+
+           return newdesc.toString();
+       }
+    }
+
+    /**
+     * Substitutes class names in the given descriptor string
+     * according to the given <code>map</code>.
+     *
+     * @param map              a map between replaced and substituted
+     *                         JVM class names.
+     *
+     * @see Descriptor#toJvmName(String)
+     */
+    public static String rename(String desc, Map map) {
+       if (map == null)
+           return desc;
+
+       StringBuffer newdesc = new StringBuffer();
+       int head = 0;
+       int i = 0;
+       for (;;) {
+           int j = desc.indexOf('L', i);
+           if (j < 0)
+               break;
+
+           int k = desc.indexOf(';', j);
+           if (k < 0)
+               break;
+
+           i = k + 1;
+           String name = desc.substring(j + 1, k);
+           String name2 = (String)map.get(name);
+           if (name2 != null) {
+               newdesc.append(desc.substring(head, j));
+               newdesc.append('L');
+               newdesc.append(name2);
+               newdesc.append(';');
+               head = i;
+           }
+       }
+
+       if (head == 0)
+           return desc;
+       else {
+           int len = desc.length();
+           if (head < len)
+               newdesc.append(desc.substring(head, len));
+
+           return newdesc.toString();
+       }
+    }
+
+    /**
+     * Returns the descriptor representing the given type.
+     */
+    public static String of(CtClass type) {
+       StringBuffer sbuf = new StringBuffer();
+       toDescriptor(sbuf, type);
+       return sbuf.toString();
+    }
+
+    private static void toDescriptor(StringBuffer desc, CtClass type) {
+       if (type.isArray()) {
+           desc.append('[');
+           try {
+               toDescriptor(desc, type.getComponentType());
+           }
+           catch (NotFoundException e) {
+               desc.append('L');
+               String name = type.getName();
+               desc.append(toJvmName(name.substring(0, name.length() - 2)));
+               desc.append(';');
+           }
+       }
+       else if (type.isPrimitive()) {
+           CtPrimitiveType pt = (CtPrimitiveType)type;
+           desc.append(pt.getDescriptor());
+       }
+       else {          // class type
+           desc.append('L');
+           desc.append(type.getName().replace('.', '/'));
+           desc.append(';');
+       }
+    }
+
+    /**
+     * Returns the descriptor representing a constructor receiving
+     * the given parameter types.
+     *
+     * @param paramTypes       parameter types
+     */
+    public static String ofConstructor(CtClass[] paramTypes) {
+       return ofMethod(CtClass.voidType, paramTypes);
+    }
+
+    /**
+     * Returns the descriptor representing a method that receives
+     * the given parameter types and returns the given type.
+     *
+     * @param returnType       return type
+     * @param paramTypes       parameter types
+     */
+    public static String ofMethod(CtClass returnType, CtClass[] paramTypes) {
+       StringBuffer desc = new StringBuffer();
+       desc.append('(');
+       if (paramTypes != null) {
+           int n = paramTypes.length;
+           for (int i = 0; i < n; ++i)
+               toDescriptor(desc, paramTypes[i]);
+       }
+
+       desc.append(')');
+       if (returnType != null)
+           toDescriptor(desc, returnType);
+
+       return desc.toString();
+    }
+
+    /**
+     * Returns the descriptor representing a list of parameter types.
+     * For example, if the given parameter types are two <code>int</code>,
+     * then this method returns <code>"(II)"</code>.
+     *
+     * @param paramTypes       parameter types
+     */
+    public static String ofParameters(CtClass[] paramTypes) {
+       return ofMethod(null, paramTypes);
+    }
+
+    /**
+     * Appends a parameter type to the parameter list represented
+     * by the given descriptor.
+     *
+     * <p><code>classname</code> must not be an array type.
+     *
+     * @param classname                parameter type (not primitive type)
+     * @param desc             descriptor
+     */
+    public static String appendParameter(String classname,
+                                        String desc) {
+       int i = desc.indexOf(')');
+       if (i < 0)
+           return desc;
+       else {
+           StringBuffer newdesc = new StringBuffer();
+           newdesc.append(desc.substring(0, i));
+           newdesc.append('L');
+           newdesc.append(classname.replace('.', '/'));
+           newdesc.append(';');
+           newdesc.append(desc.substring(i));
+           return newdesc.toString();
+       }
+    }
+
+    /**
+     * Inserts a parameter type at the beginning of the parameter
+     * list represented
+     * by the given descriptor.
+     *
+     * <p><code>classname</code> must not be an array type.
+     *
+     * @param classname                parameter type (not primitive type)
+     * @param desc             descriptor
+     */
+    public static String insertParameter(String classname,
+                                        String desc) {
+       if (desc.charAt(0) != '(')
+           return desc;
+       else
+           return "(L" + classname.replace('.', '/') + ';'
+               + desc.substring(1);
+    }
+
+    /**
+     * Changes the return type included in the given descriptor.
+     *
+     * <p><code>classname</code> must not be an array type.
+     *
+     * @param classname                return type
+     * @param desc             descriptor
+     */
+    public static String changeReturnType(String classname, String desc) {
+       int i = desc.indexOf(')');
+       if (i < 0)
+           return desc;
+       else {
+           StringBuffer newdesc = new StringBuffer();
+           newdesc.append(desc.substring(0, i + 1));
+           newdesc.append('L');
+           newdesc.append(classname.replace('.', '/'));
+           newdesc.append(';');
+           return newdesc.toString();
+       }
+    }
+
+    /**
+     * Returns the <code>CtClass</code> objects representing the parameter
+     * types specified by the given descriptor.
+     *
+     * @param desc     descriptor
+     * @param cp       the class pool used for obtaining
+     *                 a <code>CtClass</code> object.
+     */
+    public static CtClass[] getParameterTypes(String desc, ClassPool cp)
+       throws NotFoundException
+    {
+       if (desc.charAt(0) != '(')
+           return null;
+       else {
+           int num = numOfParameters(desc);
+           CtClass[] args = new CtClass[num];
+           int n = 0;
+           int i = 1;
+           do {
+               i = toCtClass(cp, desc, i, args, n++);
+           } while(i > 0);
+           return args;
+       }
+    }
+
+    /**
+     * Returns the <code>CtClass</code> object representing the return
+     * type specified by the given descriptor.
+     *
+     * @param desc     descriptor
+     * @param cp       the class pool used for obtaining
+     *                 a <code>CtClass</code> object.
+     */
+    public static CtClass getReturnType(String desc, ClassPool cp)
+       throws NotFoundException
+    {
+       int i = desc.indexOf(')');
+       if (i < 0)
+           return null;
+       else {
+           CtClass[] type = new CtClass[1];
+           toCtClass(cp, desc, i + 1, type, 0);
+           return type[0];
+       }
+    }
+
+    /**
+     * Returns the number of the prameters included in the given
+     * descriptor.
+     *
+     * @param desc     descriptor
+     */
+    public static int numOfParameters(String desc) {
+       int n = 0;
+       int i = 1;
+       for (;;) {
+           char c = desc.charAt(i);
+           if (c == ')')
+               break;
+
+           while (c == '[')
+               c = desc.charAt(++i);
+
+           if (c == 'L') {
+               i = desc.indexOf(';', i) + 1;
+               if (i <= 0)
+                   throw new IndexOutOfBoundsException("bad descriptor");
+           }
+           else
+               ++i;
+
+           ++n;
+       }
+
+       return n;
+    }
+
+    /**
+     * Returns a <code>CtClass</code> object representing the type
+     * specified by the given descriptor.
+     *
+     * <p>This method works even if the package-class separator is
+     * not <code>/</code> but <code>.</code> (period).  For example,
+     * it accepts <code>Ljava.lang.Object;</code>
+     * as well as <code>Ljava/lang/Object;</code>.
+     *
+     * @param desc     descriptor
+     * @param cp       the class pool used for obtaining
+     *                 a <code>CtClass</code> object.
+     */
+    public static CtClass toCtClass(String desc, ClassPool cp)
+       throws NotFoundException
+    {
+       CtClass[] clazz = new CtClass[1];
+       int res = toCtClass(cp, desc, 0, clazz, 0);
+       if (res >= 0)
+           return clazz[0];
+       else {
+           // maybe, you forgot to surround the class name with
+           // L and ;.  It violates the protocol, but I'm tolerant...
+           return cp.get(desc.replace('/', '.'));
+       }
+    }
+
+    private static int toCtClass(ClassPool cp, String desc, int i,
+                                CtClass[] args, int n)
+       throws NotFoundException
+    {
+       int i2;
+       String name;
+
+       int arrayDim = 0;
+       char c = desc.charAt(i);
+       while (c == '[') {
+           ++arrayDim;
+           c = desc.charAt(++i);
+       }
+
+       if (c == 'L') {
+           i2 = desc.indexOf(';', ++i);
+           name = desc.substring(i, i2++).replace('/', '.');
+       }
+       else {
+           CtClass type = toPrimitiveClass(c);
+           if (type == null)
+               return -1;      // error
+
+           i2 = i + 1;
+           if (arrayDim == 0) {
+               args[n] = type;
+               return i2;      // neither an array type or a class type
+           }
+           else
+               name = type.getName();
+       }
+
+       if (arrayDim > 0) {
+           StringBuffer sbuf = new StringBuffer(name);
+           while (arrayDim-- > 0)
+               sbuf.append("[]");
+
+           name = sbuf.toString();
+       }
+
+       args[n] = cp.get(name);
+       return i2;
+    }
+
+    private static CtClass toPrimitiveClass(char c) {
+       CtClass type = null;
+       switch (c) {
+       case 'Z' :
+           type = CtClass.booleanType;
+           break;
+       case 'C' :
+           type = CtClass.charType;
+           break;
+       case 'B' :
+           type = CtClass.byteType;
+           break;
+       case 'S' :
+           type = CtClass.shortType;
+           break;
+       case 'I' :
+           type = CtClass.intType;
+           break;
+       case 'J' :
+           type = CtClass.longType;
+           break;
+       case 'F' :
+           type = CtClass.floatType;
+           break;
+       case 'D' :
+           type = CtClass.doubleType;
+           break;
+       case 'V' :
+           type = CtClass.voidType;
+           break;
+       }
+
+       return type;
+    }
+
+    /**
+     * Computes the data size specified by the given descriptor.
+     * For example, if the descriptor is "D", this method returns 2.
+     *
+     * <p>If the descriptor represents a method type, this method returns
+     * (the size of the returned value) - (the sum of the data sizes
+     * of all the parameters).  For example, if the descriptor is
+     * "(I)D", then this method returns 1 (= 2 - 1).
+     *
+     * @param desc     descriptor
+     */
+    public static int dataSize(String desc) {
+       int n = 0;
+       char c = desc.charAt(0);
+       if (c == '(') {
+           int i = 1;
+           for (;;) {
+               c = desc.charAt(i);
+               if (c == ')') {
+                   c = desc.charAt(i + 1);
+                   break;
+               }
+
+               boolean array = false;
+               while (c == '[') {
+                   array = true;
+                   c = desc.charAt(++i);
+               }
+
+               if (c == 'L') {
+                   i = desc.indexOf(';', i) + 1;
+                   if (i <= 0)
+                       throw new IndexOutOfBoundsException("bad descriptor");
+               }
+               else
+                   ++i;
+
+               if (!array && (c == 'J' || c == 'D'))
+                   n -= 2;
+               else
+                   --n;
+           }
+       }
+
+       if (c == 'J' || c == 'D')
+           n += 2;
+       else if (c != 'V')
+           ++n;
+
+       return n;
+    }
+}
diff --git a/src/main/javassist/bytecode/ExceptionTable.java b/src/main/javassist/bytecode/ExceptionTable.java
new file mode 100644 (file)
index 0000000..71baa3b
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+
+class ExceptionTableEntry {
+    int startPc;
+    int endPc;
+    int handlerPc;
+    int catchType;
+
+    ExceptionTableEntry(int start, int end, int handle, int type) {
+       startPc = start;
+       endPc = end;
+       handlerPc = handle;
+       catchType = type;
+    }
+}
+
+/**
+ * <code>exception_table[]</code> of <code>Code_attribute</code>.
+ */
+public class ExceptionTable {
+    private ConstPool constPool;
+    private ArrayList entries;
+
+    /**
+     * Constructs an <code>exception_table[]</code>.
+     *
+     * @param cp       constant pool table.
+     */
+    public ExceptionTable(ConstPool cp) {
+       constPool = cp;
+       entries = new ArrayList();
+    }
+
+    ExceptionTable(ConstPool cp, DataInputStream in) throws IOException {
+       constPool = cp;
+       int length = in.readUnsignedShort();
+       ArrayList list = new ArrayList(length);
+       for (int i = 0; i < length; ++i) {
+           int start = in.readUnsignedShort();
+           int end = in.readUnsignedShort();
+           int handle = in.readUnsignedShort();
+           int type = in.readUnsignedShort();
+           list.add(new ExceptionTableEntry(start, end, handle, type));
+       }
+
+       entries = list;
+    }
+
+    /**
+     * Returns <code>exception_table_length</code>, which is the number
+     * of entries in the <code>exception_table[]</code>.
+     */
+    public int size() {
+       return entries.size();
+    }
+
+    /**
+     * Returns <code>startPc</code> of the <i>n</i>-th entry.
+     *
+     * @param nth              the <i>n</i>-th (&gt;= 0).
+     */
+    public int startPc(int nth) {
+       ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
+       return e.startPc;
+    }
+
+    /**
+     * Sets <code>startPc</code> of the <i>n</i>-th entry.
+     *
+     * @param nth              the <i>n</i>-th (&gt;= 0).
+     * @param value            new value.
+     */
+    public void setStartPc(int nth, int value) {
+       ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
+       e.startPc = value;
+    }
+
+    /**
+     * Returns <code>endPc</code> of the <i>n</i>-th entry.
+     *
+     * @param nth              the <i>n</i>-th (&gt;= 0).
+     */
+    public int endPc(int nth) {
+       ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
+       return e.endPc;
+    }
+
+    /**
+     * Sets <code>endPc</code> of the <i>n</i>-th entry.
+     *
+     * @param nth              the <i>n</i>-th (&gt;= 0).
+     * @param value            new value.
+     */
+    public void setEndPc(int nth, int value) {
+       ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
+       e.endPc = value;
+    }
+
+    /**
+     * Returns <code>handlerPc</code> of the <i>n</i>-th entry.
+     *
+     * @param nth              the <i>n</i>-th (&gt;= 0).
+     */
+    public int handlerPc(int nth) {
+       ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
+       return e.handlerPc;
+    }
+
+    /**
+     * Sets <code>handlerPc</code> of the <i>n</i>-th entry.
+     *
+     * @param nth              the <i>n</i>-th (&gt;= 0).
+     * @param value            new value.
+     */
+    public void setHandlerPc(int nth, int value) {
+       ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
+       e.handlerPc = value;
+    }
+
+    /**
+     * Returns <code>catchType</code> of the <i>n</i>-th entry.
+     *
+     * @param nth              the <i>n</i>-th (&gt;= 0).
+     */
+    public int catchType(int nth) {
+       ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
+       return e.catchType;
+    }
+
+    /**
+     * Sets <code>catchType</code> of the <i>n</i>-th entry.
+     *
+     * @param nth              the <i>n</i>-th (&gt;= 0).
+     * @param value            new value.
+     */
+    public void setCatchType(int nth, int value) {
+       ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth);
+       e.catchType = value;
+    }
+
+    /**
+     * Copies the given exception table at the specified position
+     * in the table.
+     *
+     * @param index    index (&gt;= 0) at which the entry is to be inserted.
+     * @param offset   the offset added to the code position.
+     */
+    public void add(int index, ExceptionTable table, int offset) {
+       int len = table.size();
+       while (--len >= 0) {
+           ExceptionTableEntry e
+               = (ExceptionTableEntry)table.entries.get(len);
+           add(index, e.startPc + offset, e.endPc + offset,
+               e.handlerPc + offset, e.catchType);
+       }
+    }
+
+    /**
+     * Adds a new entry at the specified position in the table.
+     *
+     * @param index    index (&gt;= 0) at which the entry is to be inserted.
+     * @param start    <code>startPc</code>
+     * @param end      <code>endPc</code>
+     * @param handler  <code>handlerPc</code>
+     * @param type     <code>catchType</code>
+     */
+    public void add(int index, int start, int end, int handler, int type) {
+       entries.add(index,
+                   new ExceptionTableEntry(start, end, handler, type));
+    }
+
+    /**
+     * Appends a new entry at the end of the table.
+     *
+     * @param start    <code>startPc</code>
+     * @param end      <code>endPc</code>
+     * @param handler  <code>handlerPc</code>
+     * @param type     <code>catchType</code>
+     */
+    public void add(int start, int end, int handler, int type) {
+       entries.add(new ExceptionTableEntry(start, end, handler, type));
+    }
+
+    /**
+     * Removes the entry at the specified position in the table.
+     *
+     * @param index    the index of the removed entry.
+     */
+    public void remove(int index) {
+       entries.remove(index);
+    }
+
+    /**
+     * Makes a copy of this <code>exception_table[]</code>.
+     * Class names are replaced according to the
+     * given <code>Map</code> object.
+     *
+     * @param newCp    the constant pool table used by the new copy.
+     * @param classnames       pairs of replaced and substituted
+     *                         class names.
+     */
+    public ExceptionTable copy(ConstPool newCp, Map classnames) {
+       ExceptionTable et = new ExceptionTable(newCp);
+       ConstPool srcCp = constPool;
+       int len = size();
+       for (int i = 0; i < len; ++i) {
+           ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i);
+           int type = srcCp.copy(e.catchType, newCp, classnames);
+           et.add(e.startPc, e.endPc, e.handlerPc, type);
+       }
+
+       return et;
+    }
+
+    void shiftPc(int where, int gapLength, boolean exclusive) {
+       int len = size();
+       for (int i = 0; i < len; ++i) {
+           ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i);
+           e.startPc = shiftPc(e.startPc, where, gapLength, exclusive);
+           e.endPc = shiftPc(e.endPc, where, gapLength, exclusive);
+           e.handlerPc = shiftPc(e.handlerPc, where, gapLength, exclusive);
+       }
+    }
+
+    private static int shiftPc(int pc, int where, int gapLength,
+                              boolean exclusive) {
+       if (pc > where || (exclusive && pc == where))
+           pc += gapLength;
+
+       return pc;
+    }
+
+    void write(DataOutputStream out) throws IOException {
+       int len = size();
+       out.writeShort(len);            // exception_table_length
+       for (int i = 0; i < len; ++i) {
+           ExceptionTableEntry e = (ExceptionTableEntry)entries.get(i);
+           out.writeShort(e.startPc);
+           out.writeShort(e.endPc);
+           out.writeShort(e.handlerPc);
+           out.writeShort(e.catchType);
+       }
+    }
+}
diff --git a/src/main/javassist/bytecode/ExceptionsAttribute.java b/src/main/javassist/bytecode/ExceptionsAttribute.java
new file mode 100644 (file)
index 0000000..db304e7
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <code>Exceptions_attribute</code>.
+ */
+public class ExceptionsAttribute extends AttributeInfo {
+    /**
+     * The name of this attribute <code>"Exceptions"</code>.
+     */
+    public static final String tag = "Exceptions";
+
+    ExceptionsAttribute(ConstPool cp, int n, DataInputStream in)
+       throws IOException
+    {
+       super(cp, n, in);
+    }
+
+    /**
+     * Constructs a copy of an exceptions attribute.
+     *
+     * @param cp               constant pool table.
+     * @param src              source attribute.
+     */
+    private ExceptionsAttribute(ConstPool cp, ExceptionsAttribute src,
+                               Map classnames) {
+       super(cp, tag);
+       copyFrom(src, classnames);
+    }
+
+    /**
+     * Constructs a new exceptions attribute.
+     *
+     * @param cp               constant pool table.
+     */
+    public ExceptionsAttribute(ConstPool cp) {
+       super(cp, tag);
+       byte[] data = new byte[2];
+       data[0] = data[1] = 0;  // empty
+       this.info = data;
+    }
+
+    /**
+     * Makes a copy.  Class names are replaced according to the
+     * given <code>Map</code> object.
+     *
+     * @param newCp    the constant pool table used by the new copy.
+     * @param classnames       pairs of replaced and substituted
+     *                         class names.
+     */
+    public AttributeInfo copy(ConstPool newCp, Map classnames) {
+       return new ExceptionsAttribute(newCp, this, classnames);
+    }
+
+    /**
+     * Copies the contents from a source attribute.
+     * Specified class names are replaced during the copy.
+     *
+     * @param srcAttr          source Exceptions attribute
+     * @param classnames       pairs of replaced and substituted
+     *                         class names.
+     */
+    private void copyFrom(ExceptionsAttribute srcAttr, Map classnames) {
+       ConstPool srcCp = srcAttr.constPool;
+       ConstPool destCp = this.constPool;
+       byte[] src = srcAttr.info;
+       int num = src.length;
+       byte[] dest = new byte[num];
+       dest[0] = src[0];
+       dest[1] = src[1];       // the number of elements.
+       for (int i = 2; i < num; i += 2) {
+           int index = ByteArray.readU16bit(src, i);
+           ByteArray.write16bit(srcCp.copy(index, destCp, classnames),
+                                dest, i);
+       }
+
+       this.info = dest;
+    }
+
+    /**
+     * Returns <code>exception_index_table[]</code>.
+     */
+    public int[] getExceptionIndexes() {
+       byte[] blist = info;
+       int n = blist.length;
+       if (n <= 2)
+           return null;
+
+       int[] elist = new int[n / 2 - 1];
+       int k = 0;
+       for (int j = 2; j < n; j += 2)
+           elist[k++] = ((blist[j] & 0xff) << 8) | (blist[j + 1] & 0xff);
+
+       return elist;
+    }
+
+    /**
+     * Returns the names of exceptions that the method may throw.
+     */
+    public String[] getExceptions() {
+       byte[] blist = info;
+       int n = blist.length;
+       if (n <= 2)
+           return null;
+
+       String[] elist = new String[n / 2 - 1];
+       int k = 0;
+       for (int j = 2; j < n; j += 2) {
+           int index = ((blist[j] & 0xff) << 8) | (blist[j + 1] & 0xff);
+           elist[k++] = constPool.getClassInfo(index);
+       }
+
+       return elist;
+    }
+
+    /**
+     * Sets <code>exception_index_table[]</code>.
+     */
+    public void setExceptionIndexes(int[] elist) {
+       int n = elist.length;
+       byte[] blist = new byte[n * 2 + 2];
+       ByteArray.write16bit(n, blist, 0);
+       for (int i = 0; i < n; ++i)
+           ByteArray.write16bit(elist[i], blist, i * 2 + 2);
+
+       info = blist;
+    }
+
+    /**
+     * Sets the names of exceptions that the method may throw.
+     */
+    public void setExceptions(String[] elist) {
+       int n = elist.length;
+       byte[] blist = new byte[n * 2 + 2];
+       ByteArray.write16bit(n, blist, 0);
+       for (int i = 0; i < n; ++i)
+           ByteArray.write16bit(constPool.addClassInfo(elist[i]),
+                                blist, i * 2 + 2);
+
+       info = blist;
+    }
+
+    /**
+     * Returns <code>number_of_exceptions</code>.
+     */
+    public int length() { return info.length / 2 - 1; }
+
+    /**
+     * Returns the value of <code>exception_index_table[nth]</code>.
+     */
+    public int getException(int nth) {
+       int index = nth * 2 + 2;        // nth >= 0
+       return ((info[index] & 0xff) << 8) | (info[index + 1] & 0xff);
+    }
+}
diff --git a/src/main/javassist/bytecode/FieldInfo.java b/src/main/javassist/bytecode/FieldInfo.java
new file mode 100644 (file)
index 0000000..d5e3591
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.LinkedList;
+
+/**
+ * <code>field_info</code> structure.
+ *
+ * @see javassist.CtField#getFieldInfo()
+ */
+public final class FieldInfo {
+    ConstPool constPool;
+    int accessFlags;
+    int name;
+    int descriptor;
+    LinkedList attribute;      // may be null.
+
+    private FieldInfo(ConstPool cp) {
+       constPool = cp;
+       accessFlags = 0;
+       attribute = null;
+    }
+
+    /**
+     * Constructs a <code>field_info</code> structure.
+     *
+     * @param cp               a constant pool table
+     * @param fieldName                field name
+     * @param desc             field descriptor
+     *
+     * @see Descriptor
+     */
+    public FieldInfo(ConstPool cp, String fieldName, String desc) {
+       this(cp);
+       name = cp.addUtf8Info(fieldName);
+       descriptor = cp.addUtf8Info(desc);
+    }
+
+    FieldInfo(ConstPool cp, DataInputStream in) throws IOException {
+       this(cp);
+       read(in);
+    }
+
+    /**
+     * Returns the constant pool table used
+     * by this <code>field_info</code>.
+     */
+    public ConstPool getConstPool() { return constPool; }
+
+    /**
+     * Returns the field name.
+     */
+    public String getName() {
+       return constPool.getUtf8Info(name);
+    }
+
+    /**
+     * Sets the field name.
+     */
+    public void setName(String newName) {
+       name = constPool.addUtf8Info(newName);
+    }
+
+    /**
+     * Returns the access flags.
+     *
+     * @see AccessFlag
+     */
+    public int getAccessFlags() {
+       return accessFlags;
+    }
+
+    /**
+     * Sets the access flags.
+     *
+     * @see AccessFlag
+     */
+    public void setAccessFlags(int acc) {
+       accessFlags = acc;
+    }
+
+    /**
+     * Returns the field descriptor.
+     *
+     * @see Descriptor
+     */
+    public String getDescriptor() {
+       return constPool.getUtf8Info(descriptor);
+    }
+
+    /**
+     * Sets the field descriptor.
+     *
+     * @see Descriptor
+     */
+    public void setDescriptor(String desc) {
+       if (!desc.equals(getDescriptor()))
+           descriptor = constPool.addUtf8Info(desc);
+    }
+
+    /**
+     * Returns all the attributes.
+     *
+     * @return a list of <code>AttributeInfo</code> objects.
+     * @see AttributeInfo
+     */
+    public List getAttributes() {
+       if (attribute == null)
+           attribute = new LinkedList();
+
+       return attribute;
+    }
+
+    /**
+     * Returns the attribute with the specified name.
+     *
+     * @param name     attribute name
+     */
+    public AttributeInfo getAttribute(String name) {
+       return AttributeInfo.lookup(attribute, name);
+    }
+
+    /**
+     * Appends an attribute.  If there is already an attribute with
+     * the same name, the new one substitutes for it.
+     */
+    public void addAttribute(AttributeInfo info) {
+       if (attribute == null)
+           attribute = new LinkedList();
+
+       AttributeInfo.remove(attribute, info.getName());
+       attribute.add(info);
+    }
+
+    private void read(DataInputStream in) throws IOException {
+       accessFlags = in.readUnsignedShort();
+       name = in.readUnsignedShort();
+       descriptor = in.readUnsignedShort();
+       int n = in.readUnsignedShort();
+       attribute = new LinkedList();
+       for (int i = 0; i < n; ++i)
+           attribute.add(AttributeInfo.read(constPool, in));
+    }
+
+    void write(DataOutputStream out) throws IOException {
+       out.writeShort(accessFlags);
+       out.writeShort(name);
+       out.writeShort(descriptor);
+       if (attribute == null)
+           out.writeShort(0);
+       else {
+           out.writeShort(attribute.size());
+           AttributeInfo.writeAll(attribute, out);
+       }
+    }
+}
diff --git a/src/main/javassist/bytecode/InnerClassesAttribute.java b/src/main/javassist/bytecode/InnerClassesAttribute.java
new file mode 100644 (file)
index 0000000..954a166
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.util.Map;
+import java.io.IOException;
+
+/**
+ * <code>InnerClasses_attribute</code>.
+ */
+public class InnerClassesAttribute extends AttributeInfo {
+    /**
+     * The name of this attribute <code>"InnerClasses"</code>.
+     */
+    public static final String tag = "InnerClasses";
+
+    InnerClassesAttribute(ConstPool cp, int n, DataInputStream in)
+       throws IOException
+    {
+       super(cp, n, in);
+    }
+
+    private InnerClassesAttribute(ConstPool cp, byte[] info) {
+       super(cp, tag, info);
+    }
+
+    /**
+     * Returns <code>number_of_classes</code>.
+     */
+    public int length() { return ByteArray.readU16bit(get(), 0); }
+
+    /**
+     * Returns <code>classes[nth].inner_class_info_index</code>.
+     */
+    public int innerClass(int nth) {
+       return ByteArray.readU16bit(get(), nth * 8 + 2);
+    }
+
+    /**
+     * Returns <code>classes[nth].outer_class_info_index</code>.
+     */
+    public int outerClass(int nth) {
+       return ByteArray.readU16bit(get(), nth * 8 + 4);
+    }
+
+    /**
+     * Returns <code>classes[nth].inner_name_index</code>.
+     */
+    public int innerName(int nth) {
+       return ByteArray.readU16bit(get(), nth * 8 + 6);
+    }
+
+    /**
+     * Returns <code>classes[nth].inner_class_access_flags</code>.
+     */
+    public int accessFlags(int nth) {
+       return ByteArray.readU16bit(get(), nth * 8 + 8);
+    }
+
+    /**
+     * Makes a copy.  Class names are replaced according to the
+     * given <code>Map</code> object.
+     *
+     * @param newCp    the constant pool table used by the new copy.
+     * @param classnames       pairs of replaced and substituted
+     *                         class names.
+     */
+    public AttributeInfo copy(ConstPool newCp, Map classnames) {
+       byte[] src = get();
+       byte[] dest = new byte[src.length];
+       ConstPool cp = getConstPool();
+       InnerClassesAttribute attr = new InnerClassesAttribute(newCp, dest);
+       int n = ByteArray.readU16bit(src, 0);
+       ByteArray.write16bit(n, dest, 0);
+       int j = 2;
+       for (int i = 0; i < n; ++i) {
+           int innerClass = ByteArray.readU16bit(src, j);
+           int outerClass = ByteArray.readU16bit(src, j + 2);
+           int innerName = ByteArray.readU16bit(src, j + 4);
+           int innerAccess = ByteArray.readU16bit(src, j + 6);
+
+           if (innerClass != 0)
+               innerClass = cp.copy(innerClass, newCp, classnames);
+
+           ByteArray.write16bit(innerClass, dest, j);
+
+           if (outerClass != 0)
+               outerClass = cp.copy(outerClass, newCp, classnames);
+
+           ByteArray.write16bit(outerClass, dest, j + 2);
+
+           if (innerName != 0)
+               innerName = cp.copy(innerName, newCp, classnames);
+
+           ByteArray.write16bit(innerName, dest, j + 4);
+           ByteArray.write16bit(innerAccess, dest, j + 6);
+           j += 8;
+       }
+
+       return attr;
+    }
+}
diff --git a/src/main/javassist/bytecode/LineNumberAttribute.java b/src/main/javassist/bytecode/LineNumberAttribute.java
new file mode 100644 (file)
index 0000000..35f1a02
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <code>LineNumberTablec_attribute</code>.
+ */
+public class LineNumberAttribute extends AttributeInfo {
+    /**
+     * The name of this attribute <code>"LineNumberTable"</code>.
+     */
+    public static final String tag = "LineNumberTable";
+
+    LineNumberAttribute(ConstPool cp, int n, DataInputStream in)
+       throws IOException
+    {
+       super(cp, n, in);
+    }
+
+    private LineNumberAttribute(ConstPool cp, byte[] i) {
+       super(cp, tag, i);
+    }
+
+    /**
+     * Returns <code>line_number_table_length</code>.
+     * This represents the number of entries in the table.
+     */
+    public int tableLength() {
+       return ByteArray.readU16bit(info, 0);
+    }
+
+    /**
+     * Returns <code>line_number_table[i].start_pc</code>.
+     * This represents the index into the code array at which the code
+     * for a new line in the original source file begins.
+     *
+     * @param i                the i-th entry.
+     */
+    public int startPc(int i) {
+       return ByteArray.readU16bit(info, i * 4 + 2);
+    }
+
+    /**
+     * Returns <code>line_number_table[i].line_number</code>.
+     * This represents the corresponding line number in the original
+     * source file.
+     *
+     * @param i                the i-th entry.
+     */
+    public int lineNumber(int i) {
+       return ByteArray.readU16bit(info, i * 4 + 4);
+    }
+
+    /**
+     * Returns the line number corresponding to the specified bytecode.
+     *
+     * @param pc       the index into the code array.
+     */
+    public int toLineNumber(int pc) {
+       int n = tableLength();
+       int i = 0;
+       for (; i < n; ++i)
+           if (pc < startPc(i))
+               if (i == 0)
+                   return lineNumber(0);
+               else
+                   break;
+
+       return lineNumber(i - 1);
+    }
+
+    /**
+     * Returns the index into the code array at which the code for
+     * the specified line begins.
+     *
+     * @param line     the line number.
+     * @return         -1 if the specified line is not found.
+     */
+    public int toStartPc(int line) {
+       int n = tableLength();
+       for (int i = 0; i < n; ++i)
+           if (line == lineNumber(i))
+               return startPc(i);
+
+       return -1;
+    }
+
+    /**
+     * Makes a copy.
+     *
+     * @param newCp    the constant pool table used by the new copy.
+     * @param classnames       should be null.
+     */
+    public AttributeInfo copy(ConstPool newCp, Map classnames) {
+       byte[] src = info;
+       int num = src.length;
+       byte[] dest = new byte[num];
+       for (int i = 0; i < num; ++i)
+           dest[i] = src[i];
+
+       LineNumberAttribute attr = new LineNumberAttribute(newCp, dest);
+       return attr;
+    }
+}
diff --git a/src/main/javassist/bytecode/LongVector.java b/src/main/javassist/bytecode/LongVector.java
new file mode 100644 (file)
index 0000000..86ba793
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+final class LongVector {
+    private int num;
+    private Object[] objects;
+    private LongVector next;
+
+    public LongVector(int initialSize) {
+       num = 0;
+       objects = new Object[initialSize];
+       next = null;
+    }
+
+    public void addElement(Object obj) {
+       LongVector p = this;
+       while (p.next != null)
+           p = p.next;
+
+       if (p.num < p.objects.length)
+           p.objects[p.num++] = obj;
+       else {
+           LongVector q = p.next = new LongVector(p.objects.length);
+           q.objects[q.num++] = obj;
+       }
+    }
+
+    public int size() {
+       LongVector p = this;
+       int s = 0;
+       while (p != null) {
+           s += p.num;
+           p = p.next;
+       }
+
+       return s;
+    }
+
+    public Object elementAt(int i) {
+       LongVector p = this;
+       while (p != null)
+           if (i < p.num)
+               return p.objects[i];
+           else {
+               i -= p.num;
+               p = p.next;
+           }
+
+       return null;
+    }
+
+/*
+    public static void main(String [] args) {
+       LongVector v = new LongVector(4);
+       int i;
+       for (i = 0; i < 128; ++i)
+           v.addElement(new Integer(i));
+
+       System.out.println(v.size());
+       for (i = 0; i < v.size(); ++i) {
+           System.out.print(v.elementAt(i));
+           System.out.print(", ");
+       }
+    }
+*/
+}
diff --git a/src/main/javassist/bytecode/MethodInfo.java b/src/main/javassist/bytecode/MethodInfo.java
new file mode 100644 (file)
index 0000000..567529a
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.List;
+import java.util.LinkedList;
+import javassist.CannotCompileException;
+
+/**
+ * <code>method_info</code> structure.
+ *
+ * @see javassist.CtMethod#getMethodInfo()
+ * @see javassist.CtConstructor#getMethodInfo()
+ */
+public final class MethodInfo {
+    ConstPool constPool;
+    int accessFlags;
+    int name;
+    int descriptor;
+    LinkedList attribute;      // may be null
+
+    /**
+     * The name of constructors: <code>&lt;init&gt</code>.
+     */
+    public static final String nameInit = "<init>";
+
+    /**
+     * The name of class initializer (static initializer):
+     * <code>&lt;clinit&gt</code>.
+     */
+    public static final String nameClinit = "<clinit>";
+
+    private MethodInfo(ConstPool cp) {
+       constPool = cp;
+       attribute = null;
+    }
+
+    /**
+     * Constructs a <code>method_info</code> structure.
+     *
+     * @param cp               a constant pool table
+     * @param methodname       method name
+     * @param desc             method descriptor
+     *
+     * @see Descriptor
+     */
+    public MethodInfo(ConstPool cp, String methodname, String desc) {
+       this(cp);
+       accessFlags = 0;
+       name = cp.addUtf8Info(methodname);
+       descriptor = constPool.addUtf8Info(desc);
+    }
+
+    MethodInfo(ConstPool cp, DataInputStream in) throws IOException {
+       this(cp);
+       read(in);
+    }
+
+    /**
+     * Constructs a copy of <code>method_info</code> structure.
+     * Class names appearing in the source <code>method_info</code>
+     * are renamed according to <code>classnameMap</code>.
+     *
+     * <p>Note: only <code>Code</code> and <code>Exceptions</code>
+     * attributes are copied from the source.  The other attributes
+     * are ignored.
+     *
+     * @param cp               a constant pool table
+     * @param methodname       a method name
+     * @param src              a source <code>method_info</code>
+     * @param classnameMap     specifies pairs of replaced and substituted
+     *                         name.
+     * @see Descriptor
+     */
+    public MethodInfo(ConstPool cp, String methodname, MethodInfo src,
+                     Map classnameMap) throws BadBytecode
+    {
+       this(cp);
+       read(src, methodname, classnameMap);
+    }
+
+    /**
+     * Returns a method name.
+     */
+    public String getName() {
+       return constPool.getUtf8Info(name);
+    }
+
+    /**
+     * Sets a method name.
+     */
+    public void setName(String newName) {
+       name = constPool.addUtf8Info(newName);
+    }
+
+    /**
+     * Returns true if this is not a constructor or a class initializer
+     * (static initializer).
+     */
+    public boolean isMethod() {
+       String n = getName();
+       return !n.equals(nameInit) && !n.equals(nameClinit);
+    }
+
+    /**
+     * Returns a constant pool table used by this method.
+     */
+    public ConstPool getConstPool() { return constPool; }
+
+    /**
+     * Returns true if this is a constructor.
+     */
+    public boolean isConstructor() { return getName().equals(nameInit); }
+
+    /**
+     * Returns true if this is a class initializer (static initializer).
+     */
+    public boolean isStaticInitializer() {
+       return getName().equals(nameClinit);
+    }
+
+    /**
+     * Returns access flags.
+     *
+     * @see AccessFlag
+     */
+    public int getAccessFlags() {
+       return accessFlags;
+    }
+
+    /**
+     * Sets access flags.
+     *
+     * @see AccessFlag
+     */
+    public void setAccessFlags(int acc) {
+       accessFlags = acc;
+    }
+
+    /**
+     * Returns a method descriptor.
+     *
+     * @see Descriptor
+     */
+    public String getDescriptor() {
+       return constPool.getUtf8Info(descriptor);
+    }
+
+    /**
+     * Sets a method descriptor.
+     *
+     * @see Descriptor
+     */
+    public void setDescriptor(String desc) {
+       if (!desc.equals(getDescriptor()))
+           descriptor = constPool.addUtf8Info(desc);
+    }
+
+    /**
+     * Returns all the attributes.
+     *
+     * @return a list of <code>AttributeInfo</code> objects.
+     * @see AttributeInfo
+     */
+    public List getAttributes() {
+       if (attribute == null)
+           attribute = new LinkedList();
+
+       return attribute;
+    }
+
+    /**
+     * Returns the attribute with the specified name.
+     * If it is not found, this method returns null.
+     *
+     * @param name     attribute name
+     * @return         an <code>AttributeInfo</code> object or null.
+     */
+    public AttributeInfo getAttribute(String name) {
+       return AttributeInfo.lookup(attribute, name);
+    }
+
+    /**
+     * Appends an attribute.  If there is already an attribute with
+     * the same name, the new one substitutes for it.
+     */
+    public void addAttribute(AttributeInfo info) {
+       if (attribute == null)
+           attribute = new LinkedList();
+
+       AttributeInfo.remove(attribute, info.getName());
+       attribute.add(info);
+    }
+
+    /**
+     * Returns an Exceptions attribute.
+     *
+     * @return         an Exceptions attribute
+     *                 or null if it is not specified.
+     */
+    public ExceptionsAttribute getExceptionsAttribute() {
+       AttributeInfo info
+           = AttributeInfo.lookup(attribute, ExceptionsAttribute.class);
+       return (ExceptionsAttribute)info;
+    }
+
+    /**
+     * Returns a Code attribute.
+     *
+     * @return         a Code attribute
+     *                 or null if it is not specified.
+     */
+    public CodeAttribute getCodeAttribute() {
+       AttributeInfo info
+           = AttributeInfo.lookup(attribute, CodeAttribute.class);
+       return (CodeAttribute)info;
+    }
+
+    /**
+     * Removes an Exception attribute.
+     */
+    public void removeExceptionsAttribute() {
+       AttributeInfo.remove(attribute, ExceptionsAttribute.class);
+    }
+
+    /**
+     * Adds an Exception attribute.
+     *
+     * <p>The added attribute must share the same constant pool table
+     * as this <code>method_info</code> structure.
+     */
+    public void setExceptionsAttribute(ExceptionsAttribute cattr) {
+       removeExceptionsAttribute();
+       if (attribute == null)
+           attribute = new LinkedList();
+
+       attribute.add(cattr);
+    }
+
+    /**
+     * Removes a Code attribute.
+     */
+    public void removeCodeAttribute() {
+       AttributeInfo.remove(attribute, CodeAttribute.class);
+    }
+
+    /**
+     * Adds a Code attribute.
+     *
+     * <p>The added attribute must share the same constant pool table
+     * as this <code>method_info</code> structure.
+     */
+    public void setCodeAttribute(CodeAttribute cattr) {
+       removeCodeAttribute();
+       if (attribute == null)
+           attribute = new LinkedList();
+
+       attribute.add(cattr);
+    }
+
+    /**
+     * Returns the line number of the source line corresponding to the
+     * specified bytecode contained in this method.
+     *
+     * @param pos      the position of the bytecode (&gt;= 0).
+     *                 an index into the code array.
+     * @return -1      if this information is not available.
+     */
+    public int getLineNumber(int pos) {
+       CodeAttribute ca = getCodeAttribute();
+       if (ca == null)
+           return -1;
+
+       LineNumberAttribute ainfo
+           = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
+       if (ainfo == null)
+           return -1;
+
+       return ainfo.toLineNumber(pos);
+    }
+
+    /**
+     * Changes a super constructor called by this constructor.
+     *
+     * <p>This method modifies a call to <code>super()</code>,
+     * which should be at the
+     * head of a constructor body, so that a constructor in a different
+     * super class is called.  This method does not change actural
+     * parameters.  Hence the new super class must have a constructor
+     * with the same signature as the original one.
+     *
+     * <p>This method should be called when the super class
+     * of the class declaring this method is changed.
+     *
+     * <p>This method does not perform anything unless this
+     * <code>MethodInfo</code> represents a constructor.
+     *
+     * @param superclass       the new super class
+     */
+    public void setSuperclass(String superclass) throws BadBytecode {
+       if (!isConstructor())
+           return;
+
+       CodeAttribute ca = getCodeAttribute();
+       byte[] code = ca.getCode();
+       CodeIterator iterator = ca.iterator();
+       int pos = iterator.skipSuperConstructor();
+       if (pos >= 0) {         // not this()
+           ConstPool cp = constPool;
+           int mref = ByteArray.readU16bit(code, pos + 1);
+           int nt = cp.getMethodrefNameAndType(mref);
+           int sc = cp.addClassInfo(superclass);
+           int mref2 = cp.addMethodrefInfo(sc, nt);
+           ByteArray.write16bit(mref2, code, pos + 1);
+       }
+    }
+
+    private void read(MethodInfo src, String methodname, Map classnames)
+       throws BadBytecode
+    {
+       ConstPool destCp = constPool;
+       accessFlags = src.accessFlags;
+       name = destCp.addUtf8Info(methodname);
+
+       ConstPool srcCp = src.constPool;
+       String desc = srcCp.getUtf8Info(src.descriptor);
+       String desc2 = Descriptor.rename(desc, classnames);
+       descriptor = destCp.addUtf8Info(desc2);
+
+       attribute = new LinkedList();
+       ExceptionsAttribute eattr = src.getExceptionsAttribute();
+       if (eattr != null)
+           attribute.add(eattr.copy(destCp, classnames));
+
+       CodeAttribute cattr = src.getCodeAttribute();
+       if (cattr != null)
+           attribute.add(cattr.copy(destCp, classnames));
+    }
+
+    private void read(DataInputStream in) throws IOException {
+       accessFlags = in.readUnsignedShort();
+       name = in.readUnsignedShort();
+       descriptor = in.readUnsignedShort();
+       int n = in.readUnsignedShort();
+       attribute = new LinkedList();
+       for (int i = 0; i < n; ++i)
+           attribute.add(AttributeInfo.read(constPool, in));
+    }
+
+    void write(DataOutputStream out) throws IOException {
+       out.writeShort(accessFlags);
+       out.writeShort(name);
+       out.writeShort(descriptor);
+
+       if (attribute == null)
+           out.writeShort(0);
+       else {
+           out.writeShort(attribute.size());
+           AttributeInfo.writeAll(attribute, out);
+       }
+    }
+}
diff --git a/src/main/javassist/bytecode/Mnemonic.java b/src/main/javassist/bytecode/Mnemonic.java
new file mode 100644 (file)
index 0000000..5fa4fd5
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+/**
+ * JVM Instruction Names.
+ *
+ * <p>This interface has been separated from javassist.bytecode.Opcode
+ * because typical bytecode translators do not use mnemonics.  If this
+ * interface were merged with Opcode, extra memory would be unnecessary
+ * consumed.
+ *
+ * @see Opcode
+ */
+public interface Mnemonic {
+
+    /**
+     * The instruction names (mnemonics) sorted by the opcode.
+     * The length of this array is 202 (jsr_w=201).
+     *
+     * <p>The value at index 186 is null since no instruction is
+     * assigned to 186.
+     */
+    String[] OPCODE = {
+       "nop",  /* 0*/
+       "aconst_null",  /* 1*/
+       "iconst_m1",    /* 2*/
+       "iconst_0",     /* 3*/
+       "iconst_1",     /* 4*/
+       "iconst_2",     /* 5*/
+       "iconst_3",     /* 6*/
+       "iconst_4",     /* 7*/
+       "iconst_5",     /* 8*/
+       "lconst_0",     /* 9*/
+       "lconst_1",     /* 10*/
+       "fconst_0",     /* 11*/
+       "fconst_1",     /* 12*/
+       "fconst_2",     /* 13*/
+       "dconst_0",     /* 14*/
+       "dconst_1",     /* 15*/
+       "bipush",       /* 16*/
+       "sipush",       /* 17*/
+       "ldc",  /* 18*/
+       "ldc_w",        /* 19*/
+       "ldc2_w",       /* 20*/
+       "iload",        /* 21*/
+       "lload",        /* 22*/
+       "fload",        /* 23*/
+       "dload",        /* 24*/
+       "aload",        /* 25*/
+       "iload_0",      /* 26*/
+       "iload_1",      /* 27*/
+       "iload_2",      /* 28*/
+       "iload_3",      /* 29*/
+       "lload_0",      /* 30*/
+       "lload_1",      /* 31*/
+       "lload_2",      /* 32*/
+       "lload_3",      /* 33*/
+       "fload_0",      /* 34*/
+       "fload_1",      /* 35*/
+       "fload_2",      /* 36*/
+       "fload_3",      /* 37*/
+       "dload_0",      /* 38*/
+       "dload_1",      /* 39*/
+       "dload_2",      /* 40*/
+       "dload_3",      /* 41*/
+       "aload_0",      /* 42*/
+       "aload_1",      /* 43*/
+       "aload_2",      /* 44*/
+       "aload_3",      /* 45*/
+       "iaload",       /* 46*/
+       "laload",       /* 47*/
+       "faload",       /* 48*/
+       "daload",       /* 49*/
+       "aaload",       /* 50*/
+       "baload",       /* 51*/
+       "caload",       /* 52*/
+       "saload",       /* 53*/
+       "istore",       /* 54*/
+       "lstore",       /* 55*/
+       "fstore",       /* 56*/
+       "dstore",       /* 57*/
+       "astore",       /* 58*/
+       "istore_0",     /* 59*/
+       "istore_1",     /* 60*/
+       "istore_2",     /* 61*/
+       "istore_3",     /* 62*/
+       "lstore_0",     /* 63*/
+       "lstore_1",     /* 64*/
+       "lstore_2",     /* 65*/
+       "lstore_3",     /* 66*/
+       "fstore_0",     /* 67*/
+       "fstore_1",     /* 68*/
+       "fstore_2",     /* 69*/
+       "fstore_3",     /* 70*/
+       "dstore_0",     /* 71*/
+       "dstore_1",     /* 72*/
+       "dstore_2",     /* 73*/
+       "dstore_3",     /* 74*/
+       "astore_0",     /* 75*/
+       "astore_1",     /* 76*/
+       "astore_2",     /* 77*/
+       "astore_3",     /* 78*/
+       "iastore",      /* 79*/
+       "lastore",      /* 80*/
+       "fastore",      /* 81*/
+       "dastore",      /* 82*/
+       "aastore",      /* 83*/
+       "bastore",      /* 84*/
+       "castore",      /* 85*/
+       "sastore",      /* 86*/
+       "pop",  /* 87*/
+       "pop2", /* 88*/
+       "dup",  /* 89*/
+       "dup_x1",       /* 90*/
+       "dup_x2",       /* 91*/
+       "dup2", /* 92*/
+       "dup2_x1",      /* 93*/
+       "dup2_x2",      /* 94*/
+       "swap", /* 95*/
+       "iadd", /* 96*/
+       "ladd", /* 97*/
+       "fadd", /* 98*/
+       "dadd", /* 99*/
+       "isub", /* 100*/
+       "lsub", /* 101*/
+       "fsub", /* 102*/
+       "dsub", /* 103*/
+       "imul", /* 104*/
+       "lmul", /* 105*/
+       "fmul", /* 106*/
+       "dmul", /* 107*/
+       "idiv", /* 108*/
+       "ldiv", /* 109*/
+       "fdiv", /* 110*/
+       "ddiv", /* 111*/
+       "irem", /* 112*/
+       "lrem", /* 113*/
+       "frem", /* 114*/
+       "drem", /* 115*/
+       "ineg", /* 116*/
+       "lneg", /* 117*/
+       "fneg", /* 118*/
+       "dneg", /* 119*/
+       "ishl", /* 120*/
+       "lshl", /* 121*/
+       "ishr", /* 122*/
+       "lshr", /* 123*/
+       "iushr",        /* 124*/
+       "lushr",        /* 125*/
+       "iand", /* 126*/
+       "land", /* 127*/
+       "ior",  /* 128*/
+       "lor",  /* 129*/
+       "ixor", /* 130*/
+       "lxor", /* 131*/
+       "iinc", /* 132*/
+       "i2l",  /* 133*/
+       "i2f",  /* 134*/
+       "i2d",  /* 135*/
+       "l2i",  /* 136*/
+       "l2f",  /* 137*/
+       "l2d",  /* 138*/
+       "f2i",  /* 139*/
+       "f2l",  /* 140*/
+       "f2d",  /* 141*/
+       "d2i",  /* 142*/
+       "d2l",  /* 143*/
+       "d2f",  /* 144*/
+       "i2b",  /* 145*/
+       "i2c",  /* 146*/
+       "i2s",  /* 147*/
+       "lcmp", /* 148*/
+       "fcmpl",        /* 149*/
+       "fcmpg",        /* 150*/
+       "dcmpl",        /* 151*/
+       "dcmpg",        /* 152*/
+       "ifeq", /* 153*/
+       "ifne", /* 154*/
+       "iflt", /* 155*/
+       "ifge", /* 156*/
+       "ifgt", /* 157*/
+       "ifle", /* 158*/
+       "if_icmpeq",    /* 159*/
+       "if_icmpne",    /* 160*/
+       "if_icmplt",    /* 161*/
+       "if_icmpge",    /* 162*/
+       "if_icmpgt",    /* 163*/
+       "if_icmple",    /* 164*/
+       "if_acmpeq",    /* 165*/
+       "if_acmpne",    /* 166*/
+       "goto", /* 167*/
+       "jsr",  /* 168*/
+       "ret",  /* 169*/
+       "tableswitch",  /* 170*/
+       "lookupswitch", /* 171*/
+       "ireturn",      /* 172*/
+       "lreturn",      /* 173*/
+       "freturn",      /* 174*/
+       "dreturn",      /* 175*/
+       "areturn",      /* 176*/
+       "return",       /* 177*/
+       "getstatic",    /* 178*/
+       "putstatic",    /* 179*/
+       "getfield",     /* 180*/
+       "putfield",     /* 181*/
+       "invokevirtual",        /* 182*/
+       "invokespecial",        /* 183*/
+       "invokestatic", /* 184*/
+       "invokeinterface",      /* 185*/
+       null,
+       "new",  /* 187*/
+       "newarray",     /* 188*/
+       "anewarray",    /* 189*/
+       "arraylength",  /* 190*/
+       "athrow",       /* 191*/
+       "checkcast",    /* 192*/
+       "instanceof",   /* 193*/
+       "monitorenter", /* 194*/
+       "monitorexit",  /* 195*/
+       "wide", /* 196*/
+       "multianewarray",       /* 197*/
+       "ifnull",       /* 198*/
+       "ifnonnull",    /* 199*/
+       "goto_w",       /* 200*/
+       "jsr_w"         /* 201*/
+    };
+}
diff --git a/src/main/javassist/bytecode/Opcode.java b/src/main/javassist/bytecode/Opcode.java
new file mode 100644 (file)
index 0000000..e0c21b3
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+/**
+ * JVM Instruction Set.
+ *
+ * <p>This interface defines opcodes and
+ * array types for the NEWARRAY instruction.
+ *
+ * @see Mnemonic
+ */
+public interface Opcode {
+    /* Opcodes */
+
+    int AALOAD = 50;
+    int AASTORE = 83;
+    int ACONST_NULL = 1;
+    int ALOAD = 25;
+    int ALOAD_0 = 42;
+    int ALOAD_1 = 43;
+    int ALOAD_2 = 44;
+    int ALOAD_3 = 45;
+    int ANEWARRAY = 189;
+    int ARETURN = 176;
+    int ARRAYLENGTH = 190;
+    int ASTORE = 58;
+    int ASTORE_0 = 75;
+    int ASTORE_1 = 76;
+    int ASTORE_2 = 77;
+    int ASTORE_3 = 78;
+    int ATHROW = 191;
+    int BALOAD = 51;
+    int BASTORE = 84;
+    int BIPUSH = 16;
+    int CALOAD = 52;
+    int CASTORE = 85;
+    int CHECKCAST = 192;
+    int D2F = 144;
+    int D2I = 142;
+    int D2L = 143;
+    int DADD = 99;
+    int DALOAD = 49;
+    int DASTORE = 82;
+    int DCMPG = 152;
+    int DCMPL = 151;
+    int DCONST_0 = 14;
+    int DCONST_1 = 15;
+    int DDIV = 111;
+    int DLOAD = 24;
+    int DLOAD_0 = 38;
+    int DLOAD_1 = 39;
+    int DLOAD_2 = 40;
+    int DLOAD_3 = 41;
+    int DMUL = 107;
+    int DNEG = 119;
+    int DREM = 115;
+    int DRETURN = 175;
+    int DSTORE = 57;
+    int DSTORE_0 = 71;
+    int DSTORE_1 = 72;
+    int DSTORE_2 = 73;
+    int DSTORE_3 = 74;
+    int DSUB = 103;
+    int DUP = 89;
+    int DUP2 = 92;
+    int DUP2_X1 = 93;
+    int DUP2_X2 = 94;
+    int DUP_X1 = 90;
+    int DUP_X2 = 91;
+    int F2D = 141;
+    int F2I = 139;
+    int F2L = 140;
+    int FADD = 98;
+    int FALOAD = 48;
+    int FASTORE = 81;
+    int FCMPG = 150;
+    int FCMPL = 149;
+    int FCONST_0 = 11;
+    int FCONST_1 = 12;
+    int FCONST_2 = 13;
+    int FDIV = 110;
+    int FLOAD = 23;
+    int FLOAD_0 = 34;
+    int FLOAD_1 = 35;
+    int FLOAD_2 = 36;
+    int FLOAD_3 = 37;
+    int FMUL = 106;
+    int FNEG = 118;
+    int FREM = 114;
+    int FRETURN = 174;
+    int FSTORE = 56;
+    int FSTORE_0 = 67;
+    int FSTORE_1 = 68;
+    int FSTORE_2 = 69;
+    int FSTORE_3 = 70;
+    int FSUB = 102;
+    int GETFIELD = 180;
+    int GETSTATIC = 178;
+    int GOTO = 167;
+    int GOTO_W = 200;
+    int I2B = 145;
+    int I2C = 146;
+    int I2D = 135;
+    int I2F = 134;
+    int I2L = 133;
+    int I2S = 147;
+    int IADD = 96;
+    int IALOAD = 46;
+    int IAND = 126;
+    int IASTORE = 79;
+    int ICONST_0 = 3;
+    int ICONST_1 = 4;
+    int ICONST_2 = 5;
+    int ICONST_3 = 6;
+    int ICONST_4 = 7;
+    int ICONST_5 = 8;
+    int ICONST_M1 = 2;
+    int IDIV = 108;
+    int IFEQ = 153;
+    int IFGE = 156;
+    int IFGT = 157;
+    int IFLE = 158;
+    int IFLT = 155;
+    int IFNE = 154;
+    int IFNONNULL = 199;
+    int IFNULL = 198;
+    int IF_ACMPEQ = 165;
+    int IF_ACMPNE = 166;
+    int IF_ICMPEQ = 159;
+    int IF_ICMPGE = 162;
+    int IF_ICMPGT = 163;
+    int IF_ICMPLE = 164;
+    int IF_ICMPLT = 161;
+    int IF_ICMPNE = 160;
+    int IINC = 132;
+    int ILOAD = 21;
+    int ILOAD_0 = 26;
+    int ILOAD_1 = 27;
+    int ILOAD_2 = 28;
+    int ILOAD_3 = 29;
+    int IMUL = 104;
+    int INEG = 116;
+    int INSTANCEOF = 193;
+    int INVOKEINTERFACE = 185;
+    int INVOKESPECIAL = 183;
+    int INVOKESTATIC = 184;
+    int INVOKEVIRTUAL = 182;
+    int IOR = 128;
+    int IREM = 112;
+    int IRETURN = 172;
+    int ISHL = 120;
+    int ISHR = 122;
+    int ISTORE = 54;
+    int ISTORE_0 = 59;
+    int ISTORE_1 = 60;
+    int ISTORE_2 = 61;
+    int ISTORE_3 = 62;
+    int ISUB = 100;
+    int IUSHR = 124;
+    int IXOR = 130;
+    int JSR = 168;
+    int JSR_W = 201;
+    int L2D = 138;
+    int L2F = 137;
+    int L2I = 136;
+    int LADD = 97;
+    int LALOAD = 47;
+    int LAND = 127;
+    int LASTORE = 80;
+    int LCMP = 148;
+    int LCONST_0 = 9;
+    int LCONST_1 = 10;
+    int LDC = 18;
+    int LDC2_W = 20;
+    int LDC_W = 19;
+    int LDIV = 109;
+    int LLOAD = 22;
+    int LLOAD_0 = 30;
+    int LLOAD_1 = 31;
+    int LLOAD_2 = 32;
+    int LLOAD_3 = 33;
+    int LMUL = 105;
+    int LNEG = 117;
+    int LOOKUPSWITCH = 171;
+    int LOR = 129;
+    int LREM = 113;
+    int LRETURN = 173;
+    int LSHL = 121;
+    int LSHR = 123;
+    int LSTORE = 55;
+    int LSTORE_0 = 63;
+    int LSTORE_1 = 64;
+    int LSTORE_2 = 65;
+    int LSTORE_3 = 66;
+    int LSUB = 101;
+    int LUSHR = 125;
+    int LXOR = 131;
+    int MONITORENTER = 194;
+    int MONITOREXIT = 195;
+    int MULTIANEWARRAY = 197;
+    int NEW = 187;
+    int NEWARRAY = 188;
+    int NOP = 0;
+    int POP = 87;
+    int POP2 = 88;
+    int PUTFIELD = 181;
+    int PUTSTATIC = 179;
+    int RET = 169;
+    int RETURN = 177;
+    int SALOAD = 53;
+    int SASTORE = 86;
+    int SIPUSH = 17;
+    int SWAP = 95;
+    int TABLESWITCH = 170;
+    int WIDE = 196;
+
+    /* array-type code for the newarray instruction */
+
+    int T_BOOLEAN = 4;
+    int T_CHAR = 5;
+    int T_FLOAT = 6;
+    int T_DOUBLE = 7;
+    int T_BYTE = 8;
+    int T_SHORT = 9;
+    int T_INT = 10;
+    int T_LONG = 11;
+
+    /* how many values are pushed on the operand stack. */
+    int[] STACK_GROW = {
+       0, // nop, 0
+       1, // aconst_null, 1
+       1, // iconst_m1, 2
+       1, // iconst_0, 3
+       1, // iconst_1, 4
+       1, // iconst_2, 5
+       1, // iconst_3, 6
+       1, // iconst_4, 7
+       1, // iconst_5, 8
+       2, // lconst_0, 9
+       2, // lconst_1, 10
+       1, // fconst_0, 11
+       1, // fconst_1, 12
+       1, // fconst_2, 13
+       2, // dconst_0, 14
+       2, // dconst_1, 15
+       1, // bipush, 16
+       1, // sipush, 17
+       1, // ldc, 18
+       1, // ldc_w, 19
+       2, // ldc2_w, 20
+       1, // iload, 21
+       2, // lload, 22
+       1, // fload, 23
+       2, // dload, 24
+       1, // aload, 25
+       1, // iload_0, 26
+       1, // iload_1, 27
+       1, // iload_2, 28
+       1, // iload_3, 29
+       2, // lload_0, 30
+       2, // lload_1, 31
+       2, // lload_2, 32
+       2, // lload_3, 33
+       1, // fload_0, 34
+       1, // fload_1, 35
+       1, // fload_2, 36
+       1, // fload_3, 37
+       2, // dload_0, 38
+       2, // dload_1, 39
+       2, // dload_2, 40
+       2, // dload_3, 41
+       1, // aload_0, 42
+       1, // aload_1, 43
+       1, // aload_2, 44
+       1, // aload_3, 45
+       -1, // iaload, 46
+       0, // laload, 47
+       -1, // faload, 48
+       0, // daload, 49
+       -1, // aaload, 50
+       -1, // baload, 51
+       -1, // caload, 52
+       -1, // saload, 53
+       -1, // istore, 54
+       -2, // lstore, 55
+       -1, // fstore, 56
+       -2, // dstore, 57
+       -1, // astore, 58
+       -1, // istore_0, 59
+       -1, // istore_1, 60
+       -1, // istore_2, 61
+       -1, // istore_3, 62
+       -2, // lstore_0, 63
+       -2, // lstore_1, 64
+       -2, // lstore_2, 65
+       -2, // lstore_3, 66
+       -1, // fstore_0, 67
+       -1, // fstore_1, 68
+       -1, // fstore_2, 69
+       -1, // fstore_3, 70
+       -2, // dstore_0, 71
+       -2, // dstore_1, 72
+       -2, // dstore_2, 73
+       -2, // dstore_3, 74
+       -1, // astore_0, 75
+       -1, // astore_1, 76
+       -1, // astore_2, 77
+       -1, // astore_3, 78
+       -3, // iastore, 79
+       -4, // lastore, 80
+       -3, // fastore, 81
+       -4, // dastore, 82
+       -3, // aastore, 83
+       -3, // bastore, 84
+       -3, // castore, 85
+       -3, // sastore, 86
+       -1, // pop, 87
+       -2, // pop2, 88
+       1, // dup, 89
+       1, // dup_x1, 90
+       1, // dup_x2, 91
+       2, // dup2, 92
+       2, // dup2_x1, 93
+       2, // dup2_x2, 94
+       0, // swap, 95
+       -1, // iadd, 96
+       -2, // ladd, 97
+       -1, // fadd, 98
+       -2, // dadd, 99
+       -1, // isub, 100
+       -2, // lsub, 101
+       -1, // fsub, 102
+       -2, // dsub, 103
+       -1, // imul, 104
+       -2, // lmul, 105
+       -1, // fmul, 106
+       -2, // dmul, 107
+       -1, // idiv, 108
+       -2, // ldiv, 109
+       -1, // fdiv, 110
+       -2, // ddiv, 111
+       -1, // irem, 112
+       -2, // lrem, 113
+       -1, // frem, 114
+       -2, // drem, 115
+       0, // ineg, 116
+       0, // lneg, 117
+       0, // fneg, 118
+       0, // dneg, 119
+       -1, // ishl, 120
+       -1, // lshl, 121
+       -1, // ishr, 122
+       -1, // lshr, 123
+       -1, // iushr, 124
+       -1, // lushr, 125
+       -1, // iand, 126
+       -2, // land, 127
+       -1, // ior, 128
+       -2, // lor, 129
+       -1, // ixor, 130
+       -2, // lxor, 131
+       0, // iinc, 132
+       1, // i2l, 133
+       0, // i2f, 134
+       1, // i2d, 135
+       -1, // l2i, 136
+       -1, // l2f, 137
+       0, // l2d, 138
+       0, // f2i, 139
+       1, // f2l, 140
+       1, // f2d, 141
+       -1, // d2i, 142
+       0, // d2l, 143
+       -1, // d2f, 144
+       0, // i2b, 145
+       0, // i2c, 146
+       0, // i2s, 147
+       -3, // lcmp, 148
+       -1, // fcmpl, 149
+       -1, // fcmpg, 150
+       -3, // dcmpl, 151
+       -3, // dcmpg, 152
+       -1, // ifeq, 153
+       -1, // ifne, 154
+       -1, // iflt, 155
+       -1, // ifge, 156
+       -1, // ifgt, 157
+       -1, // ifle, 158
+       -2, // if_icmpeq, 159
+       -2, // if_icmpne, 160
+       -2, // if_icmplt, 161
+       -2, // if_icmpge, 162
+       -2, // if_icmpgt, 163
+       -2, // if_icmple, 164
+       -2, // if_acmpeq, 165
+       -2, // if_acmpne, 166
+       0, // goto, 167
+       1, // jsr, 168
+       0, // ret, 169
+       -1, // tableswitch, 170
+       -1, // lookupswitch, 171
+       -1, // ireturn, 172
+       -2, // lreturn, 173
+       -1, // freturn, 174
+       -2, // dreturn, 175
+       -1, // areturn, 176
+       0, // return, 177
+       0, // getstatic, 178            depends on the type
+       0, // putstatic, 179            depends on the type
+       0, // getfield, 180             depends on the type
+       0, // putfield, 181             depends on the type
+       0, // invokevirtual, 182        depends on the type
+       0, // invokespecial, 183        depends on the type
+       0, // invokestatic, 184         depends on the type
+       0, // invokeinterface, 185      depends on the type
+       0, // undefined, 186
+       1, // new, 187
+       0, // newarray, 188
+       0, // anewarray, 189
+       0, // arraylength, 190
+       0, // athrow, 191               stack is cleared
+       0, // checkcast, 192
+       0, // instanceof, 193
+       -1, // monitorenter, 194
+       -1, // monitorexit, 195
+       0, // wide, 196                 depends on the following opcode
+       0, // multianewarray, 197       depends on the dimensions
+       -1, // ifnull, 198
+       -1, // ifnonnull, 199
+       0, // goto_w, 200
+       1 // jsr_w, 201
+    };
+}
diff --git a/src/main/javassist/bytecode/SourceFileAttribute.java b/src/main/javassist/bytecode/SourceFileAttribute.java
new file mode 100644 (file)
index 0000000..22e6f6f
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <code>SourceFile_attribute</code>.
+ */
+public class SourceFileAttribute extends AttributeInfo {
+    /**
+     * The name of this attribute <code>"SourceFile"</code>.
+     */
+    public static final String tag = "SourceFile";
+
+    SourceFileAttribute(ConstPool cp, int n, DataInputStream in)
+       throws IOException
+    {
+       super(cp, n, in);
+    }
+
+    /**
+     * Constructs a SourceFile attribute.
+     *
+     * @param cp               a constant pool table.
+     * @param filename         the name of the source file.
+     */
+    public SourceFileAttribute(ConstPool cp, String filename) {
+       super(cp, tag);
+       int index = cp.addUtf8Info(filename);
+       byte[] bvalue = new byte[2];
+       bvalue[0] = (byte)(index >>> 8);
+       bvalue[1] = (byte)index;
+       set(bvalue);
+    }
+
+    /**
+     * Returns the file name indicated by <code>sourcefile_index</code>.
+     */
+    public String getFileName() {
+       return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0));
+    }
+
+    /**
+     * Makes a copy.  Class names are replaced according to the
+     * given <code>Map</code> object.
+     *
+     * @param newCp    the constant pool table used by the new copy.
+     * @param classnames       pairs of replaced and substituted
+     *                         class names.
+     */
+    public AttributeInfo copy(ConstPool newCp, Map classnames) {
+       return new SourceFileAttribute(newCp, getFileName());
+    }
+}
diff --git a/src/main/javassist/bytecode/SyntheticAttribute.java b/src/main/javassist/bytecode/SyntheticAttribute.java
new file mode 100644 (file)
index 0000000..63d559c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <code>Synthetic_attribute</code>.
+ */
+public class SyntheticAttribute extends AttributeInfo {
+    /**
+     * The name of this attribute <code>"Synthetic"</code>.
+     */
+    public static final String tag = "Synthetic";
+
+    SyntheticAttribute(ConstPool cp, int n, DataInputStream in)
+       throws IOException
+    {
+       super(cp, n, in);
+    }
+
+    /**
+     * Constructs a Synthetic attribute.
+     *
+     * @param cp               a constant pool table.
+     * @param filename         the name of the source file.
+     */
+    public SyntheticAttribute(ConstPool cp) {
+       super(cp, tag, new byte[0]);
+    }
+
+    /**
+     * Makes a copy.
+     *
+     * @param newCp    the constant pool table used by the new copy.
+     * @param classnames       should be null.
+     */
+    public AttributeInfo copy(ConstPool newCp, Map classnames) {
+       return new SyntheticAttribute(newCp);
+    }
+}
diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java
new file mode 100644 (file)
index 0000000..9a5f677
--- /dev/null
@@ -0,0 +1,1559 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+import java.util.ArrayList;
+import javassist.compiler.ast.*;
+import javassist.bytecode.*;
+
+/* The code generator is implemeted by three files:
+ * CodeGen.java, MemberCodeGen.java, and JvstCodeGen.
+ * I just wanted to split a big file into three smaller ones.
+ */
+
+public abstract class CodeGen extends Visitor implements Opcode, TokenId {
+    static final String javaLangObject = "java.lang.Object";
+    static final String jvmJavaLangObject = "java/lang/Object";
+
+    static final String javaLangString = "java.lang.String";
+    static final String jvmJavaLangString = "java/lang/String";
+
+    protected Bytecode bytecode;
+    private int tempVar;
+
+    /**
+     * Must be true if compilation is for a static method.
+     */
+    public boolean inStaticMethod;
+
+    protected ArrayList breakList, continueList;
+
+    /* The following fields are used by atXXX() methods
+     * for returning the type of the compiled expression.
+     */
+    protected int exprType;    // VOID, NULL, CLASS, BOOLEAN, INT, ...
+    protected int arrayDim;
+    protected String className;        // JVM-internal representation
+
+    public CodeGen(Bytecode b) {
+       bytecode = b;
+       tempVar = -1;
+       inStaticMethod = false;
+       breakList = null;
+       continueList = null;
+    }
+
+    protected static void fatal() throws CompileError {
+       throw new CompileError("fatal");
+    }
+
+    public static boolean is2word(int type, int dim) {
+       return dim == 0 && (type == DOUBLE || type == LONG); 
+    }
+
+    public int getMaxLocals() { return bytecode.getMaxLocals(); }
+
+    public void setMaxLocals(int n) {
+       bytecode.setMaxLocals(n);
+    }
+
+    protected void incMaxLocals(int size) {
+       bytecode.incMaxLocals(size);
+    }
+
+    /**
+     * Returns a local variable that single or double words can be
+     * stored in.
+     */
+    protected int getTempVar() {
+       if (tempVar < 0) {
+           tempVar = getMaxLocals();
+           incMaxLocals(2);
+       }
+
+       return tempVar;
+    }
+
+    protected int getLocalVar(Declarator d) {
+       int v = d.getLocalVar();
+       if (v < 0) {
+           v = getMaxLocals(); // delayed variable allocation.
+           d.setLocalVar(v);
+           incMaxLocals(1);
+       }
+
+       return v;
+    }
+
+    /**
+     * Returns the JVM-internal representation of this class name.
+     */
+    protected abstract String getThisName();
+
+    /**
+     * Returns the JVM-internal representation of this super class name.
+     */
+    protected abstract String getSuperName() throws CompileError;
+
+    /* Converts a class name into a JVM-internal representation.
+     *
+     * It may also expand a simple class name to java.lang.*.
+     * For example, this converts Object into java/lang/Object.
+     */
+    protected abstract String resolveClassName(ASTList name)
+       throws CompileError;
+
+    /* Expands a simple class name to java.lang.*.
+     * For example, this converts Object into java/lang/Object.
+     */
+    protected abstract String resolveClassName(String jvmClassName)
+       throws CompileError;
+
+    /**
+     * @param name     the JVM-internal representation.
+     *                 name is not exapnded to java.lang.*.
+     */
+    protected static String toJvmArrayName(String name, int dim) {
+       if (name == null)
+           return null;
+
+       if (dim == 0)
+           return name;
+       else {
+           StringBuffer sbuf = new StringBuffer();
+           int d = dim;
+           while (d-- > 0)
+               sbuf.append('[');
+
+           sbuf.append('L');
+           sbuf.append(name);
+           sbuf.append(';');
+
+           return sbuf.toString();
+       }
+    }
+
+    protected static String toJvmTypeName(int type, int dim) {
+       char c = 'I';
+       switch(type) {
+       case BOOLEAN :
+           c = 'Z';
+           break;
+       case BYTE :
+           c = 'B';
+           break;
+       case CHAR :
+           c = 'C';
+           break;
+       case SHORT :
+           c = 'S';
+           break;
+       case INT :
+           c = 'I';
+           break;
+       case LONG :
+           c = 'J';
+           break;
+       case FLOAT :
+           c = 'F';
+           break;
+       case DOUBLE :
+           c = 'D';
+           break;
+       case VOID :
+           c = 'V';
+           break;
+       }
+
+       StringBuffer sbuf = new StringBuffer();
+       while (dim-- > 0)
+               sbuf.append('[');
+
+       sbuf.append(c);
+       return sbuf.toString();
+    }
+
+
+    public void atASTList(ASTList n) throws CompileError { fatal(); }
+    
+    public void atPair(Pair n) throws CompileError { fatal(); }
+
+    public void atSymbol(Symbol n) throws CompileError { fatal(); }
+
+    public void atFieldDecl(FieldDecl field) throws CompileError {
+       field.getInit().accept(this);
+    }
+
+    public void atMethodDecl(MethodDecl method) throws CompileError {
+       ASTList mods = method.getModifiers();
+       setMaxLocals(1);
+       while (mods != null) {
+           Keyword k = (Keyword)mods.head();
+           mods = mods.tail();
+           if (k.get() == STATIC) {
+               setMaxLocals(0);
+               inStaticMethod = true;
+           }
+       }
+           
+       ASTList params = method.getParams();
+       while (params != null) {
+           atDeclarator((Declarator)params.head());
+           params = params.tail();
+       }
+
+       Stmnt s = method.getBody();
+       atMethodBody(s, method.isConstructor(),
+                    method.getReturn().getType() == VOID);
+    }
+
+    public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid)
+       throws CompileError
+    {
+       if (s == null)
+           return;
+
+       if (isCons && needsSuperCall(s))
+           insertDefaultSuperCall();
+
+       s.accept(this);
+       if (isVoid
+           && (bytecode.read(bytecode.currentPc() - 1) & 0xff)
+               != Opcode.RETURN) {
+           bytecode.addOpcode(Opcode.RETURN);
+       }
+    }
+
+    private boolean needsSuperCall(Stmnt body) {
+       if (body.getOperator() == BLOCK) {
+           Stmnt first = (Stmnt)body.head();
+           if (first != null && first.getOperator() == EXPR) {
+               ASTree expr = first.head();
+               if (expr != null && expr instanceof Expr
+                   && ((Expr)expr).getOperator() == CALL) {
+                   ASTree target = ((Expr)expr).head();
+                   if (target instanceof Keyword) {
+                       int token = ((Keyword)target).get();
+                       return token != THIS && token != SUPER;
+                   }
+               }
+           }
+       }
+
+       return true;
+    }
+
+    protected abstract void insertDefaultSuperCall() throws CompileError;
+
+    public void atStmnt(Stmnt st) throws CompileError {
+       if (st == null)
+           return;     // empty
+
+       int op = st.getOperator();
+       if (op == EXPR) {
+           ASTree expr = st.getLeft();
+           if (expr instanceof AssignExpr)
+               atAssignExpr((AssignExpr)expr, false);
+           else if (isPlusPlusExpr(expr)) {
+               Expr e = (Expr)expr;
+               atPlusPlus(e.getOperator(), e.oprand1(), e, false);
+           }
+           else {
+               expr.accept(this);
+               if (is2word(exprType, arrayDim))
+                   bytecode.addOpcode(POP2);
+               else if (exprType != VOID)
+                   bytecode.addOpcode(POP);
+           }
+       }
+       else if (op == DECL || op == BLOCK) {
+           ASTList list = st;
+           while (list != null) {
+               ASTree h = list.head();
+               list = list.tail();
+               if (h != null)
+                   h.accept(this);
+           }
+       }
+       else if (op == IF)
+           atIfStmnt(st);
+       else if (op == WHILE || op == DO)
+           atWhileStmnt(st, op == WHILE);
+       else if (op == FOR)
+           atForStmnt(st);
+       else if (op == BREAK || op == CONTINUE)
+           atBreakStmnt(st, op == BREAK);
+       else if (op == TokenId.RETURN)
+           atReturnStmnt(st);
+       else if (op == THROW)
+           atThrowStmnt(st);
+       else if (op == TRY)
+           atTryStmnt(st);
+       else // LABEL, SWITCH   label stament might be null?.
+           throw new CompileError(
+               "sorry, not supported statement: TokenId " + op);
+    }
+
+    private void atIfStmnt(Stmnt st) throws CompileError {
+       ASTree expr = st.head();
+       Stmnt thenp = (Stmnt)st.tail().head();
+       Stmnt elsep = (Stmnt)st.tail().tail().head();
+       booleanExpr(false, expr);
+       int pc = bytecode.currentPc();
+       int pc2 = 0;
+       bytecode.addIndex(0);   // correct later
+
+       if (thenp != null)
+           thenp.accept(this);
+
+       if (elsep != null) {
+           bytecode.addOpcode(Opcode.GOTO);
+           pc2 = bytecode.currentPc();
+           bytecode.addIndex(0);
+       }
+
+       bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
+
+       if (elsep != null) {
+           elsep.accept(this);
+           bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
+       }
+    }
+
+    private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError {
+       ArrayList prevBreakList = breakList;
+       ArrayList prevContList = continueList;
+       breakList = new ArrayList();
+       continueList = new ArrayList();
+
+       ASTree expr = st.head();
+       Stmnt body = (Stmnt)st.tail();
+
+       int pc = 0;
+       if (notDo) {
+           bytecode.addOpcode(Opcode.GOTO);
+           pc = bytecode.currentPc();
+           bytecode.addIndex(0);
+       }
+
+       int pc2 = bytecode.currentPc();
+       if (body != null)
+           body.accept(this);
+
+       int pc3 = bytecode.currentPc();
+       if (notDo)
+           bytecode.write16bit(pc, pc3 - pc + 1);
+
+       booleanExpr(true, expr);
+       bytecode.addIndex(pc2 - bytecode.currentPc() + 1);
+
+       patchGoto(breakList, bytecode.currentPc());
+       patchGoto(continueList, pc3);
+       continueList = prevContList;
+       breakList = prevBreakList;
+    }
+
+    private void patchGoto(ArrayList list, int targetPc) {
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           int pc = ((Integer)list.get(i)).intValue();
+           bytecode.write16bit(pc, targetPc - pc + 1);
+       }
+    }
+
+    private void atForStmnt(Stmnt st) throws CompileError {
+       ArrayList prevBreakList = breakList;
+       ArrayList prevContList = continueList;
+       breakList = new ArrayList();
+       continueList = new ArrayList();
+
+       Stmnt init = (Stmnt)st.head();
+       ASTList p = st.tail();
+       ASTree expr = p.head();
+       p = p.tail();
+       Stmnt update = (Stmnt)p.head();
+       Stmnt body = (Stmnt)p.tail();
+
+       if (init != null)
+           init.accept(this);
+
+       int pc = bytecode.currentPc();
+       int pc2 = 0;
+       if (expr != null) {
+           booleanExpr(false, expr);
+           pc2 = bytecode.currentPc();
+           bytecode.addIndex(0);
+       }
+
+       if (body != null)
+           body.accept(this);
+
+       int pc3 = bytecode.currentPc();
+       if (update != null)
+           update.accept(this);
+
+       bytecode.addOpcode(Opcode.GOTO);
+       bytecode.addIndex(pc - bytecode.currentPc() + 1);
+
+       int pc4 = bytecode.currentPc();
+       if (expr != null)
+           bytecode.write16bit(pc2, pc4 - pc2 + 1);
+
+       patchGoto(breakList, pc4);
+       patchGoto(continueList, pc3);
+       continueList = prevContList;
+       breakList = prevBreakList;
+    }
+
+    private void atBreakStmnt(Stmnt st, boolean notCont)
+       throws CompileError
+    {
+       if (st.head() != null)
+           throw new CompileError(
+                       "sorry, not support labeled break or continue");
+
+       bytecode.addOpcode(Opcode.GOTO);
+       Integer pc = new Integer(bytecode.currentPc());
+       bytecode.addIndex(0);
+       if (notCont)
+           breakList.add(pc);
+       else
+           continueList.add(pc);
+    }
+
+    protected void atReturnStmnt(Stmnt st) throws CompileError {
+       atReturnStmnt2(st.getLeft());
+    }
+
+    protected final void atReturnStmnt2(ASTree result) throws CompileError {
+       int op;
+       if (result == null)
+           op = Opcode.RETURN;
+       else {
+           result.accept(this);
+           if (arrayDim > 0)
+               op = ARETURN;
+           else {
+               int type = exprType;
+               if (type == DOUBLE)
+                   op = DRETURN;
+               else if (type == FLOAT)
+                   op = FRETURN;
+               else if (type == LONG)
+                   op = LRETURN;
+               else if (isRefType(type))
+                   op = ARETURN;
+               else
+                   op = IRETURN;
+           }
+       }
+
+       bytecode.addOpcode(op);
+    }
+
+    private void atThrowStmnt(Stmnt st) throws CompileError {
+       ASTree e = st.getLeft();
+       e.accept(this);
+       if (exprType != CLASS || arrayDim > 0)
+           throw new CompileError("bad throw statement");
+
+       bytecode.addOpcode(ATHROW);
+    }
+
+    protected abstract void atTryStmnt(Stmnt st) throws CompileError;
+
+    private static boolean isPlusPlusExpr(ASTree expr) {
+       if (expr instanceof Expr) {
+           int op = ((Expr)expr).getOperator();
+           return op == PLUSPLUS || op == MINUSMINUS;
+       }
+
+       return false;
+    }
+
+    public void atDeclarator(Declarator d) throws CompileError {
+       d.setLocalVar(getMaxLocals());
+       d.setClassName(resolveClassName(d.getClassName()));
+
+       int size;
+       if (is2word(d.getType(), d.getArrayDim()))
+           size = 2;
+       else
+           size = 1;
+
+       incMaxLocals(size);
+
+       /*  NOTE: Array initializers has not been supported.
+        */
+       ASTree init = d.getInitializer();
+       if (init != null)
+           atVariableAssign(null, '=', null, d, init, false);
+    }
+
+    public abstract void atNewExpr(NewExpr n) throws CompileError;
+
+    public void atAssignExpr(AssignExpr expr) throws CompileError {
+       atAssignExpr(expr, true);
+    }
+
+    protected void atAssignExpr(AssignExpr expr, boolean doDup)
+       throws CompileError
+    {
+       // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>=
+       int op = expr.getOperator();
+       ASTree left = expr.oprand1();
+       ASTree right = expr.oprand2();
+       if (left instanceof Variable)
+           atVariableAssign(expr, op, (Variable)left,
+                            ((Variable)left).getDeclarator(),
+                            right, doDup);
+       else {
+           if (left instanceof Expr) {
+               Expr e = (Expr)left;
+               if (e.getOperator() == ARRAY) {
+                   atArrayAssign(expr, op, (Expr)left, right, doDup);
+                   return;
+               }
+           }
+
+           atFieldAssign(expr, op, left, right, doDup);
+       }
+    }
+
+    protected static void badAssign(Expr expr) throws CompileError {
+       String msg;
+       if (expr == null)
+           msg = "incompatible type for assignment";
+       else
+           msg = "incompatible type for " + expr.getName();
+
+       throw new CompileError(msg);
+    }
+
+    /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=.
+     *
+     * expr and var can be null.
+     */
+    private void atVariableAssign(Expr expr, int op, Variable var,
+                                 Declarator d, ASTree right,
+                                 boolean doDup) throws CompileError
+    {
+       int varType = d.getType();
+       int varArray = d.getArrayDim();
+       String varClass = d.getClassName();
+       int varNo = getLocalVar(d);
+
+       if (op != '=')
+           atVariable(var);
+
+       atAssignCore(expr, op, right, varType, varArray, varClass);
+
+       if (doDup)
+           if (is2word(varType, varArray))
+               bytecode.addOpcode(DUP2);
+           else
+               bytecode.addOpcode(DUP);
+
+       if (varArray > 0)
+           bytecode.addAstore(varNo);
+       else if (varType == DOUBLE)
+           bytecode.addDstore(varNo);
+       else if (varType == FLOAT)
+           bytecode.addFstore(varNo);
+       else if (varType == LONG)
+           bytecode.addLstore(varNo);
+       else if (isRefType(varType))
+           bytecode.addAstore(varNo);
+       else
+           bytecode.addIstore(varNo);
+
+       exprType = varType;
+       arrayDim = varArray;
+       className = varClass;
+    }
+
+    private void atArrayAssign(Expr expr, int op, Expr array,
+                       ASTree right, boolean doDup) throws CompileError
+    {
+       arrayAccess(array.oprand1(), array.oprand2());
+
+       if (op != '=') {
+           bytecode.addOpcode(DUP2);
+           bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
+       }
+
+       int aType = exprType;
+       int aDim = arrayDim;
+       String cname = className;
+
+       atAssignCore(expr, op, right, aType, aDim, cname);
+
+       if (doDup)
+           if (is2word(aType, aDim))
+               bytecode.addOpcode(DUP2_X2);
+           else
+               bytecode.addOpcode(DUP_X2);
+
+       bytecode.addOpcode(getArrayWriteOp(aType, aDim));
+       exprType = aType;
+       arrayDim = aDim;
+       className = cname;
+    }
+
+    protected abstract void atFieldAssign(Expr expr, int op, ASTree left,
+                       ASTree right, boolean doDup) throws CompileError;
+
+    protected void atAssignCore(Expr expr, int op, ASTree right,
+                               int type, int dim, String cname)
+       throws CompileError
+    {
+       right.accept(this);
+       if (invalidDim(exprType, arrayDim, className, type, dim, cname, false)
+           || (op != '=' && dim > 0))
+           badAssign(expr);
+
+       if (op == PLUS_E && dim == 0 && type == CLASS)
+           atStringConcatExpr(expr, type, dim, cname);
+       else if (op != '=') {
+           int token = assignOps[op - MOD_E];
+           int k = lookupBinOp(token);
+           if (k < 0)
+               fatal();
+
+           atArithBinExpr(expr, token, k, type);
+       }
+
+       if (op != '=' || (dim == 0 && !isRefType(type)))
+           atNumCastExpr(exprType, type);
+
+       // type check should be done here.
+    }
+
+    private boolean invalidDim(int srcType, int srcDim, String srcClass,
+                              int destType, int destDim, String destClass,
+                              boolean isCast)
+    {
+       if (srcDim != destDim)
+           if (srcType == NULL)
+               return false;
+           else if (destDim == 0 && destType == CLASS
+                    && jvmJavaLangObject.equals(destClass))
+               return false;
+           else if (isCast && srcDim == 0 && srcType == CLASS
+                    && jvmJavaLangObject.equals(srcClass))
+               return false;
+           else
+               return true;
+
+       return false;
+    }
+
+    public void atCondExpr(CondExpr expr) throws CompileError {
+       booleanExpr(false, expr.condExpr());
+       int pc = bytecode.currentPc();
+       bytecode.addIndex(0);   // correct later
+       expr.thenExpr().accept(this);
+       bytecode.addOpcode(Opcode.GOTO);
+       int pc2 = bytecode.currentPc();
+       bytecode.addIndex(0);
+       bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
+       expr.elseExpr().accept(this);
+       bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
+    }
+
+    private final int[] binOp = {
+       '+', DADD, FADD, LADD, IADD,
+       '-', DSUB, FSUB, LSUB, ISUB,
+       '*', DMUL, FMUL, LMUL, IMUL,
+       '/', DDIV, FDIV, LDIV, IDIV,
+       '%', DREM, FREM, LREM, IREM,
+       '|', NOP,  NOP,  LOR,  IOR,
+       '^', NOP,  NOP,  LXOR, IXOR,
+       '&', NOP,  NOP,  LAND, IAND,
+       LSHIFT, NOP, NOP, LSHL, ISHL,
+       RSHIFT, NOP, NOP, LSHR, ISHR,
+       ARSHIFT, NOP, NOP, LUSHR, IUSHR };
+
+    private int lookupBinOp(int token) {
+       int[] code = binOp;
+       int s = code.length;
+       for (int k = 0; k < s; k = k + 5)
+           if (code[k] == token)
+               return k;
+
+       return -1;
+    }
+
+    public void atBinExpr(BinExpr expr) throws CompileError {
+       int token = expr.getOperator();
+
+       /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
+        */
+       int k = lookupBinOp(token);
+       if (k >= 0) {
+           expr.oprand1().accept(this);
+           int type1 = exprType;
+           int dim1 = arrayDim;
+           String cname1 = className;
+           expr.oprand2().accept(this);
+           if (dim1 != arrayDim)
+               throw new CompileError("incompatible array types");
+
+           if (token == '+' && dim1 == 0
+               && (type1 == CLASS || exprType == CLASS))
+               atStringConcatExpr(expr, type1, dim1, cname1);
+           else
+               atArithBinExpr(expr, token, k, type1);
+
+           return;
+       }
+
+       /* equation: &&, ||, ==, !=, <=, >=, <, >
+        */
+       booleanExpr(true, expr);
+       bytecode.addIndex(7);
+       bytecode.addIconst(0);  // false
+       bytecode.addOpcode(Opcode.GOTO);
+       bytecode.addIndex(4);
+       bytecode.addIconst(1);  // true
+    }
+
+    /* arrayDim values of the two oprands must be equal.
+     * If an oprand type is not a numeric type, this method
+     * throws an exception.
+     */
+    private void atArithBinExpr(Expr expr, int token,
+                               int index, int type1) throws CompileError
+    {
+       if (arrayDim != 0)
+           badTypes(expr);
+
+       int type2 = exprType;
+       if (token == LSHIFT || token == RSHIFT || token == ARSHIFT)
+           if (type2 == INT || type2 == SHORT
+               || type2 == CHAR || type2 == BYTE)
+               exprType = type1;
+           else
+               badTypes(expr);
+       else
+           convertOprandTypes(type1, type2, expr);
+
+       int p = typePrecedence(exprType);
+       if (p >= 0) {
+           int op = binOp[index + p + 1];
+           if (op != NOP) {
+               if (p == P_INT)
+                   exprType = INT;     // type1 may be BYTE, ...
+
+               bytecode.addOpcode(op);
+               return;
+           }
+       }
+
+       badTypes(expr);
+    }
+
+    private void atStringConcatExpr(Expr expr, int type1, int dim1,
+                                   String cname1) throws CompileError
+    {
+       int type2 = exprType;
+       int dim2 = arrayDim;
+       boolean type2Is2 = is2word(type2, dim2);
+       boolean type2IsString
+           = (type2 == CLASS && jvmJavaLangString.equals(className));
+
+       if (type2Is2)
+           convToString(type2, dim2);
+
+       if (is2word(type1, dim1)) {
+           bytecode.addOpcode(DUP_X2);
+           bytecode.addOpcode(POP);
+       }
+       else
+           bytecode.addOpcode(SWAP);
+
+       convToString(type1, dim1);
+       bytecode.addOpcode(SWAP);
+
+       if (!type2Is2 && !type2IsString)
+           convToString(type2, dim2);
+
+       bytecode.addInvokevirtual(javaLangString, "concat",
+                               "(Ljava/lang/String;)Ljava/lang/String;");
+       exprType = CLASS;
+       arrayDim = 0;
+       className = jvmJavaLangString;
+    }
+
+    private void convToString(int type, int dim) throws CompileError {
+       final String method = "valueOf";
+
+       if (isRefType(type) || dim > 0)
+           bytecode.addInvokestatic(javaLangString, method,
+                               "(Ljava/lang/Object;)Ljava/lang/String;");
+       else if (type == DOUBLE)
+           bytecode.addInvokestatic(javaLangString, method,
+                                    "(D)Ljava/lang/String;");
+       else if (type == FLOAT)
+           bytecode.addInvokestatic(javaLangString, method,
+                                    "(F)Ljava/lang/String;");
+       else if (type == LONG)
+           bytecode.addInvokestatic(javaLangString, method,
+                                    "(J)Ljava/lang/String;");
+       else if (type == BOOLEAN)
+           bytecode.addInvokestatic(javaLangString, method,
+                                    "(Z)Ljava/lang/String;");
+       else if (type == CHAR)
+           bytecode.addInvokestatic(javaLangString, method,
+                                    "(C)Ljava/lang/String;");
+       else if (type == VOID)
+           throw new CompileError("void type expression");
+       else /* INT, BYTE, SHORT */
+           bytecode.addInvokestatic(javaLangString, method,
+                                    "(I)Ljava/lang/String;");
+    }
+
+    /* Produces the opcode to branch if the condition is true.
+     * The oprand is not produced.
+     *
+     * If false is returned, the branch occurs if the condition is
+     * false.
+     */
+    private void booleanExpr(boolean branchIf, ASTree expr)
+       throws CompileError
+    {
+       boolean isAndAnd;
+       int op = getCompOperator(expr);
+       if (op == EQ) {         // ==, !=, ...
+           BinExpr bexpr = (BinExpr)expr;
+           int type1 = compileOprands(bexpr);
+           compareExpr(branchIf, bexpr.getOperator(), type1, bexpr);
+       }
+       else if (op == '!')
+           booleanExpr(!branchIf, ((Expr)expr).oprand1());
+       else if ((isAndAnd = (op == ANDAND)) || op == OROR) {
+           BinExpr bexpr = (BinExpr)expr;
+           booleanExpr(!isAndAnd, bexpr.oprand1());
+           int pc = bytecode.currentPc();
+           bytecode.addIndex(0);       // correct later
+
+           booleanExpr(isAndAnd, bexpr.oprand2());
+           bytecode.write16bit(pc, bytecode.currentPc() - pc + 3);
+           if (branchIf != isAndAnd) {
+               bytecode.addIndex(6);   // skip GOTO instruction
+               bytecode.addOpcode(Opcode.GOTO);
+           }
+       }
+       else {                          // others
+           expr.accept(this);
+           bytecode.addOpcode(branchIf ? IFNE : IFEQ);
+       }
+
+       exprType = BOOLEAN;
+       arrayDim = 0;
+    }
+
+    private static int getCompOperator(ASTree expr) throws CompileError {
+       if (expr instanceof Expr) {
+           Expr bexpr = (Expr)expr;
+           int token = bexpr.getOperator();
+           if (token == '!')
+               return '!';
+           else if ((bexpr instanceof BinExpr)
+                    && token != OROR && token != ANDAND
+                    && token != '&' && token != '|')
+               return EQ;      // ==, !=, ...
+           else
+               return token;
+       }
+
+       return ' ';     // others
+    }
+
+    private int compileOprands(BinExpr expr) throws CompileError {
+       expr.oprand1().accept(this);
+       int type1 = exprType;
+       int dim1 = arrayDim;
+       expr.oprand2().accept(this);
+       if (dim1 != arrayDim)
+           throw new CompileError("incompatible array types");
+
+       return type1;
+    }
+
+    private final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE,
+                                NEQ, IF_ICMPNE, IF_ICMPEQ,
+                                LE, IF_ICMPLE, IF_ICMPGT,
+                                GE, IF_ICMPGE, IF_ICMPLT,
+                                '<', IF_ICMPLT, IF_ICMPGE,
+                                '>', IF_ICMPGT, IF_ICMPLE };
+
+    private final int ifOp2[] = { EQ, IFEQ, IFNE,
+                                 NEQ, IFNE, IFEQ,
+                                 LE, IFLE, IFGT,
+                                 GE, IFGE, IFLT,
+                                 '<', IFLT, IFGE,
+                                 '>', IFGT, IFLE };
+
+    /* Produces the opcode to branch if the condition is true.
+     * The oprands are not produced.
+     *
+     * Parameter expr - compare expression ==, !=, <=, >=, <, >
+     */
+    private void compareExpr(boolean branchIf,
+                            int token, int type1, BinExpr expr)
+       throws CompileError
+    {
+       if (arrayDim == 0)
+           convertOprandTypes(type1, exprType, expr);
+
+       int p = typePrecedence(exprType);
+       if (p == P_OTHER || arrayDim > 0)
+           if (token == EQ)
+               bytecode.addOpcode(branchIf ? IF_ACMPEQ : IF_ACMPNE);
+           else if (token == NEQ)
+               bytecode.addOpcode(branchIf ? IF_ACMPNE : IF_ACMPEQ);
+           else
+               badTypes(expr);
+       else
+           if (p == P_INT) {
+               int op[] = ifOp;
+               for (int i = 0; i < op.length; i += 3)
+                   if (op[i] == token) {
+                       bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
+                       return;
+                   }
+
+               badTypes(expr);
+           }
+           else {
+               if (p == P_DOUBLE)
+                   if (token == '<' || token == LE)
+                       bytecode.addOpcode(DCMPG);
+                   else
+                       bytecode.addOpcode(DCMPL);
+               else if (p == P_FLOAT)
+                   if (token == '<' || token == LE)
+                       bytecode.addOpcode(FCMPG);
+                   else
+                       bytecode.addOpcode(FCMPL);
+               else if (p == P_LONG)
+                   bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: <
+               else
+                   fatal();
+
+               int[] op = ifOp2;
+               for (int i = 0; i < op.length; i += 3)
+                   if (op[i] == token) {
+                       bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
+                       return;
+                   }
+
+               badTypes(expr);
+           }
+    }
+
+    protected static void badTypes(Expr expr) throws CompileError {
+       throw new CompileError("invalid types for " + expr.getName());
+    }
+
+    private static final int P_DOUBLE = 0;
+    private static final int P_FLOAT = 1;
+    private static final int P_LONG = 2;
+    private static final int P_INT = 3;
+    private static final int P_OTHER = -1;
+
+    protected static boolean isRefType(int type) {
+       return type == CLASS || type == NULL;
+    }
+
+    private static int typePrecedence(int type) {
+       if (type == DOUBLE)
+           return P_DOUBLE;
+       else if (type == FLOAT)
+           return P_FLOAT;
+       else if (type == LONG)
+           return P_LONG;
+       else if (isRefType(type))
+           return P_OTHER;
+       else if (type == VOID)
+           return P_OTHER;     // this is wrong, but ...
+       else
+           return P_INT;       // BOOLEAN, BYTE, CHAR, SHORT, INT
+    }
+
+    private static final int[] castOp = {
+           /*            D    F    L    I */
+           /* double */ NOP, D2F, D2L, D2I,
+           /* float  */ F2D, NOP, F2L, F2I,
+           /* long   */ L2D, L2F, NOP, L2I,
+           /* other  */ I2D, I2F, I2L, NOP };
+
+    /* do implicit type conversion.
+     * arrayDim values of the two oprands must be zero.
+     */
+    private void convertOprandTypes(int type1, int type2, Expr expr)
+       throws CompileError
+    {
+       boolean rightStrong;
+       int type1_p = typePrecedence(type1);
+       int type2_p = typePrecedence(type2);
+
+       if (type2_p < 0 && type1_p < 0) // not primitive types
+           return;
+
+       if (type2_p < 0 || type1_p < 0) // either is not a primitive type
+           badTypes(expr);
+
+       int op, result_type;
+       if (type1_p <= type2_p) {
+           rightStrong = false;
+           exprType = type1;
+           op = castOp[type2_p * 4 + type1_p];
+           result_type = type1_p;
+       }
+       else {
+           rightStrong = true;
+           op = castOp[type1_p * 4 + type2_p];
+           result_type = type2_p;
+       }
+
+       if (rightStrong) {
+           if (result_type == P_DOUBLE || result_type == P_LONG) {
+               if (type1_p == P_DOUBLE || type1_p == P_LONG)
+                   bytecode.addOpcode(DUP2_X2);
+               else
+                   bytecode.addOpcode(DUP2_X1);
+
+               bytecode.addOpcode(POP2);
+               bytecode.addOpcode(op);
+               bytecode.addOpcode(DUP2_X2);
+               bytecode.addOpcode(POP2);
+           }
+           else if (result_type == P_FLOAT) {
+               bytecode.addOpcode(SWAP);
+               bytecode.addOpcode(op);
+               bytecode.addOpcode(SWAP);
+           }
+           else
+               fatal();
+       }
+       else if (op != NOP)
+           bytecode.addOpcode(op);
+    }
+
+    public void atCastExpr(CastExpr expr) throws CompileError {
+       String cname = resolveClassName(expr.getClassName());
+       String toClass = checkCastExpr(expr, cname);
+       int srcType = exprType;
+       exprType = expr.getType();
+       arrayDim = expr.getArrayDim();
+       className = cname;
+       if (toClass == null)
+           atNumCastExpr(srcType, exprType);   // built-in type
+       else
+           bytecode.addCheckcast(toClass);
+    }
+
+    public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
+       String cname = resolveClassName(expr.getClassName());
+       String toClass = checkCastExpr(expr, cname);
+       bytecode.addInstanceof(toClass);
+       exprType = BOOLEAN;
+       arrayDim = 0;
+    }
+
+    private String checkCastExpr(CastExpr expr, String name)
+       throws CompileError
+    {
+       final String msg = "invalid cast";
+       ASTree oprand = expr.getOprand();
+       int dim = expr.getArrayDim();
+       int type = expr.getType();
+       oprand.accept(this);
+       int srcType = exprType;
+       if (invalidDim(srcType, arrayDim, className, type, dim, name, true)
+           || srcType == VOID || type == VOID)
+           throw new CompileError(msg);
+
+       if (type == CLASS) {
+           if (!isRefType(srcType))
+               throw new CompileError(msg);
+
+           return toJvmArrayName(name, dim);
+       }
+       else
+           if (dim > 0)
+               return toJvmTypeName(type, dim);
+           else
+               return null;    // built-in type
+    }
+
+    void atNumCastExpr(int srcType, int destType)
+       throws CompileError
+    {
+       if (srcType == destType)
+           return;
+       
+       int op, op2;
+       int stype = typePrecedence(srcType);
+       int dtype = typePrecedence(destType);
+       if (0 <= stype && stype < 3)
+           op = castOp[stype * 4 + dtype];
+       else
+           op = NOP;
+
+       if (destType == DOUBLE)
+           op2 = I2D;
+       else if (destType == FLOAT)
+           op2 = I2F;
+       else if (destType == LONG)
+           op2 = I2L;
+       else if (destType == SHORT)
+           op2 = I2S;
+       else if (destType == CHAR)
+           op2 = I2C;
+       else if (destType == BYTE)
+           op2 = I2B;
+       else
+           op2 = NOP;
+
+       if (op != NOP)
+           bytecode.addOpcode(op);
+
+       if (op == NOP || op == L2I || op == F2I || op == D2I)
+           if (op2 != NOP)
+               bytecode.addOpcode(op2);
+    }
+
+    public void atExpr(Expr expr) throws CompileError {
+       // method call, array access, member access,
+       // (unary) +, (unary) -, ++, --, !, ~
+
+       int token = expr.getOperator();
+       ASTree oprand = expr.oprand1();
+       if (token == CALL)              // method call
+           atMethodCall(expr);
+       else if (token == '.')
+           if (((Symbol)expr.oprand2()).get().equals("length"))
+               atArrayLength(expr);
+           else
+               atFieldRead(expr);
+       else if (token == MEMBER) {     // field read
+           if (!atClassObject(expr))   // .class
+               atFieldRead(expr);
+       }
+       else if (token == ARRAY)
+           atArrayRead(oprand, expr.oprand2());
+       else if (token == PLUSPLUS || token == MINUSMINUS)
+           atPlusPlus(token, oprand, expr, true);
+       else if (token == '!') {
+           booleanExpr(false, expr);
+           bytecode.addIndex(7);
+           bytecode.addIconst(1);
+           bytecode.addOpcode(Opcode.GOTO);
+           bytecode.addIndex(4);
+           bytecode.addIconst(0);
+       }
+       else {
+           expr.oprand1().accept(this);
+           int type = typePrecedence(exprType);
+           if (arrayDim > 0)
+               badType(expr);
+
+           if (token == '-') {
+               if (type == P_DOUBLE)
+                   bytecode.addOpcode(DNEG);
+               else if (type == P_FLOAT)
+                   bytecode.addOpcode(FNEG);
+               else if (type == P_LONG)
+                   bytecode.addOpcode(LNEG);
+               else if (type == P_INT) {
+                   bytecode.addOpcode(INEG);
+                   exprType = INT;     // type may be BYTE, ...
+               }
+               else
+                   badType(expr);
+           }
+           else if (token == '~') {
+               if (type == P_INT) {
+                   bytecode.addIconst(-1);
+                   bytecode.addOpcode(IXOR);
+                   exprType = INT;     // type may be BYTE. ...
+               }
+               else if (type == P_LONG) {
+                   bytecode.addLconst(-1);
+                   bytecode.addOpcode(LXOR);
+               }
+               else
+                   badType(expr);
+
+           }
+           else if (token == '+') {
+               if (type == P_OTHER)
+                   badType(expr);
+
+               // do nothing. ignore.
+           }
+           else
+               fatal();
+       }
+    }
+
+    protected static void badType(Expr expr) throws CompileError {
+       throw new CompileError("invalid type for " + expr.getName());
+    }
+
+    protected abstract void atMethodCall(Expr expr) throws CompileError;
+
+    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;
+
+       throw new CompileError(".class is not supported");
+    }
+
+    public void atArrayLength(Expr expr) throws CompileError {
+       expr.oprand1().accept(this);
+       if (arrayDim == 0)
+           throw new CompileError(".length applied to a non array");
+
+       bytecode.addOpcode(ARRAYLENGTH);
+       exprType = INT;
+       arrayDim = 0;
+    }
+
+    public void atArrayRead(ASTree array, ASTree index)
+       throws CompileError
+    {
+       int op;
+       arrayAccess(array, index);
+       bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
+    }
+
+    protected void arrayAccess(ASTree array, ASTree index)
+       throws CompileError
+    {
+       array.accept(this);
+       int type = exprType;
+       int dim = arrayDim;
+       if (dim == 0)
+           throw new CompileError("bad array access");
+
+       String cname = className;
+
+       index.accept(this);
+       if (typePrecedence(exprType) != P_INT || arrayDim > 0)
+           throw new CompileError("bad array index");
+
+       exprType = type;
+       arrayDim = dim - 1;
+       className = cname;
+    }
+
+    protected static int getArrayReadOp(int type, int dim) {
+       int op;
+       if (dim > 0)
+           return AALOAD;
+
+       switch (type) {
+       case DOUBLE :
+           return DALOAD;
+       case FLOAT :
+           return FALOAD;
+       case LONG :
+           return LALOAD;
+       case INT :
+           return IALOAD;
+       case SHORT :
+           return SALOAD;
+       case CHAR :
+           return CALOAD;
+       case BYTE :
+       case BOOLEAN :
+           return BALOAD;
+       default :
+           return AALOAD;
+       }
+    }
+
+    protected static int getArrayWriteOp(int type, int dim) {
+       int op;
+       if (dim > 0)
+           return AASTORE;
+
+       switch (type) {
+       case DOUBLE :
+           return DASTORE;
+       case FLOAT :
+           return FASTORE;
+       case LONG :
+           return LASTORE;
+       case INT :
+           return IASTORE;
+       case CHAR :
+           return CASTORE;
+       case BYTE :
+       case BOOLEAN :
+           return BASTORE;
+       default :
+           return AASTORE;
+       }
+    }
+
+    private void atPlusPlus(int token, ASTree oprand, Expr expr,
+                           boolean doDup) throws CompileError
+    {
+       boolean isPost = oprand == null;        // ++i or i++?
+       if (isPost)
+           oprand = expr.oprand2();
+
+       if (oprand instanceof Variable) {
+           Declarator d = ((Variable)oprand).getDeclarator();
+           int t = exprType = d.getType();
+           arrayDim = d.getArrayDim();
+           int var = getLocalVar(d);
+           if (arrayDim > 0)
+               badType(expr);
+
+           if (t == DOUBLE) {
+               bytecode.addDload(var);
+               if (doDup && isPost)
+                   bytecode.addOpcode(DUP2);
+
+               bytecode.addDconst(1.0);
+               bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
+               if (doDup && !isPost)
+                   bytecode.addOpcode(DUP2);
+
+               bytecode.addDstore(var);
+           }
+           else if (t == LONG) {
+               bytecode.addLload(var);
+               if (doDup && isPost)
+                   bytecode.addOpcode(DUP2);
+
+               bytecode.addLconst((long)1);
+               bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
+               if (doDup && !isPost)
+                   bytecode.addOpcode(DUP2);
+
+               bytecode.addLstore(var);
+           }
+           else if (t == FLOAT) {
+               bytecode.addFload(var);
+               if (doDup && isPost)
+                   bytecode.addOpcode(DUP);
+
+               bytecode.addFconst(1.0f);
+               bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
+               if (doDup && !isPost)
+                   bytecode.addOpcode(DUP);
+
+               bytecode.addFstore(var);
+           }
+           else if (t == BYTE || t == CHAR || t == SHORT || t == INT) {
+               if (doDup && isPost)
+                   bytecode.addIload(var);
+
+               bytecode.addOpcode(IINC);
+               bytecode.add(var);
+               bytecode.add(token == PLUSPLUS ? 1 : -1);
+
+               if (doDup && !isPost)
+                   bytecode.addIload(var);
+           }
+           else
+               badType(expr);
+       }
+       else {
+           if (oprand instanceof Expr) {
+               Expr e = (Expr)oprand;
+               if (e.getOperator() == ARRAY) {
+                   atArrayPlusPlus(token, isPost, e, doDup);
+                   return;
+               }
+           }
+
+           atFieldPlusPlus(token, isPost, oprand, expr, doDup);
+       }
+    }
+
+    public void atArrayPlusPlus(int token, boolean isPost,
+                       Expr expr, boolean doDup) throws CompileError
+    {
+       arrayAccess(expr.oprand1(), expr.oprand2());
+       int t = exprType;
+       int dim = arrayDim;
+       if (dim > 0)
+           badType(expr);
+
+       bytecode.addOpcode(DUP2);
+       bytecode.addOpcode(getArrayReadOp(t, arrayDim));
+       int dup_code = is2word(t, dim) ? DUP2_X2 : DUP_X2;
+       atPlusPlusCore(dup_code, doDup, token, isPost, expr);
+       bytecode.addOpcode(getArrayWriteOp(t, dim));
+    }
+
+    protected void atPlusPlusCore(int dup_code, boolean doDup,
+                                 int token, boolean isPost,
+                                 Expr expr) throws CompileError
+    {
+       int t = exprType;
+
+       if (doDup && isPost)
+           bytecode.addOpcode(dup_code);
+
+       if (t == INT || t == BYTE || t == CHAR || t == SHORT) {
+           bytecode.addIconst(1);
+           bytecode.addOpcode(token == PLUSPLUS ? IADD : ISUB);
+           exprType = INT;
+       }
+       else if (t == LONG) {
+           bytecode.addLconst((long)1);
+           bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
+       }
+       else if (t == FLOAT) {
+           bytecode.addFconst(1.0f);
+           bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
+       }
+       else if (t == DOUBLE) {
+           bytecode.addDconst(1.0);
+           bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
+       }
+       else
+           badType(expr);
+
+       if (doDup && !isPost)
+           bytecode.addOpcode(dup_code);
+    }
+
+    protected abstract void atFieldPlusPlus(int token, boolean isPost,
+               ASTree oprand, Expr expr, boolean doDup) throws CompileError;
+
+    public abstract void atMember(Member n) throws CompileError;
+
+    public void atVariable(Variable v) throws CompileError {
+       Declarator d = v.getDeclarator();
+       exprType = d.getType();
+       arrayDim = d.getArrayDim();
+       className = d.getClassName();
+       int var = getLocalVar(d);
+
+       if (arrayDim > 0)
+           bytecode.addAload(var);
+       else
+           switch (exprType) {
+           case CLASS :
+               bytecode.addAload(var);
+               break;
+           case LONG :
+               bytecode.addLload(var);
+               break;
+           case FLOAT :
+               bytecode.addFload(var);
+               break;
+           case DOUBLE :
+               bytecode.addDload(var);
+               break;
+           default :   // BOOLEAN, BYTE, CHAR, SHORT, INT
+               bytecode.addIload(var);
+               break;
+           }
+    }
+
+    public void atKeyword(Keyword k) throws CompileError {
+       arrayDim = 0;
+       int token = k.get();
+       switch (token) {
+       case TRUE :
+           bytecode.addIconst(1);
+           exprType = BOOLEAN;
+           break;
+       case FALSE :
+           bytecode.addIconst(0);
+           exprType = BOOLEAN;
+           break;
+       case NULL :
+           bytecode.addOpcode(ACONST_NULL);
+           exprType = NULL;
+           break;
+       case THIS :
+       case SUPER :
+           if (inStaticMethod)
+               throw new CompileError("not-available: "
+                                      + (token == THIS ? "this" : "super"));
+
+           bytecode.addAload(0);
+           exprType = CLASS;
+           if (token == THIS)
+               className = getThisName();
+           else
+               className = getSuperName();             
+           break;
+       default :
+           fatal();
+       }
+    }
+
+    public void atStringL(StringL s) throws CompileError {
+       exprType = CLASS;
+       arrayDim = 0;
+       className = "java/lang/String";
+       bytecode.addLdc(s.get());
+    }
+
+    public void atIntConst(IntConst i) throws CompileError {
+       arrayDim = 0;
+       long value = i.get();
+       int type = i.getType();
+       if (type == IntConstant || type == CharConstant) {
+           exprType = (type == IntConstant ? INT : CHAR);
+           bytecode.addIconst((int)value);
+       }
+       else {
+           exprType = LONG;
+           bytecode.addLconst(value);
+       }
+    }
+
+    public void atDoubleConst(DoubleConst d) throws CompileError {
+       arrayDim = 0;
+       if (d.getType() == DoubleConstant) {
+           exprType = DOUBLE;
+           bytecode.addDconst(d.get());
+       }
+       else {
+           exprType = FLOAT;
+           bytecode.addFconst((float)d.get());
+       }
+    }
+}
diff --git a/src/main/javassist/compiler/CompileError.java b/src/main/javassist/compiler/CompileError.java
new file mode 100644 (file)
index 0000000..98b97ef
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+public class CompileError extends Exception {
+    private Lex lex;
+    private String reason;
+
+    public CompileError(String s, Lex l) {
+       reason = s;
+       lex = l;
+    }
+
+    public CompileError(String s) {
+       reason = s;
+       lex = null;
+    }
+
+    public String getMessage() {
+       return reason;
+    }
+
+    public String toString() {
+       return "compile error: " + reason;
+    }
+}
diff --git a/src/main/javassist/compiler/Javac.java b/src/main/javassist/compiler/Javac.java
new file mode 100644 (file)
index 0000000..8f15ca8
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+import javassist.CtClass;
+import javassist.CtMember;
+import javassist.CtField;
+import javassist.CtBehavior;
+import javassist.CtMethod;
+import javassist.CtConstructor;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.Modifier;
+import javassist.bytecode.Bytecode;
+import javassist.NotFoundException;
+
+import javassist.compiler.ast.*;
+
+public class Javac {
+    JvstCodeGen gen;
+    SymbolTable stable;
+    private Bytecode bytecode;
+
+    public static final String param0Name = "$0";
+    public static final String resultVarName = "$_";
+    public static final String proceedName = "$proceed";
+
+    /**
+     * Constructs a compiler.
+     *
+     * @param thisClass                the class that a compiled method/field
+     *                         belongs to.
+     */
+    public Javac(CtClass thisClass) {
+       this(new Bytecode(thisClass.getClassFile2().getConstPool(), 0, 0),
+            thisClass);
+    }
+
+    /**
+     * Constructs a compiler.
+     * The produced bytecode is stored in the <code>Bytecode</code> object
+     * specified by <code>b</code>.
+     *
+     * @param thisClass                the class that a compiled method/field
+     *                         belongs to.
+     */
+    public Javac(Bytecode b, CtClass thisClass) {
+       gen = new JvstCodeGen(b, thisClass, thisClass.getClassPool());
+       stable = new SymbolTable();
+       bytecode = b;
+    }
+
+    /**
+     * Returns the produced bytecode.
+     */
+    public Bytecode getBytecode() { return bytecode; }
+
+    /**
+     * Compiles a method, constructor, or field declaration
+     * to a class.
+     * A field declaration can declare only one field.
+     *
+     * <p>In a method or constructor body, $0, $1, ... and $_
+     * are not available.
+     *
+     * @return         a <code>CtMethod</code>, <code>CtConstructor</code>,
+     *                 or <code>CtField</code> object.
+     * @see #recordProceed(String,String)
+     */
+    public CtMember compile(String src) throws CompileError {
+       Parser p = new Parser(new Lex(src));
+       ASTList mem = p.parseMember1(stable);
+       try {
+           if (mem instanceof FieldDecl)
+               return compileField((FieldDecl)mem);
+           else
+               return compileMethod(p, (MethodDecl)mem);
+       }
+       catch (CannotCompileException e) {
+           throw new CompileError(e.getMessage());
+       }
+    }
+
+    public static class CtFieldWithInit extends CtField {
+       private ASTree init;
+
+       CtFieldWithInit(CtClass type, String name, CtClass declaring)
+           throws CannotCompileException
+       {
+           super(type, name, declaring);
+           init = null;
+       }
+
+       protected void setInit(ASTree i) { init = i; }
+
+       protected ASTree getInitAST() {
+           return init;
+       }
+    }
+
+    private CtField compileField(FieldDecl fd)
+       throws CompileError, CannotCompileException
+    {
+       CtFieldWithInit f;
+       Declarator d = fd.getDeclarator();
+       f = new CtFieldWithInit(gen.lookupClass(d), d.getVariable().get(),
+                               gen.getThisClass());
+       f.setModifiers(gen.getModifiers(fd.getModifiers()));
+       if (fd.getInit() != null)
+           f.setInit(fd.getInit());
+
+       return f;
+    }
+
+    private CtMember compileMethod(Parser p, MethodDecl md)
+       throws CompileError
+    {
+       int mod = gen.getModifiers(md.getModifiers());
+       CtClass[] plist = gen.makeParamList(md);
+       CtClass[] tlist = gen.makeThrowsList(md);
+       recordParams(plist, Modifier.isStatic(mod));
+       md = p.parseMethod2(stable, md);
+       try {
+           if (md.isConstructor()) {
+               CtConstructor cons = new CtConstructor(plist,
+                                                  gen.getThisClass());
+               cons.setModifiers(mod);
+               md.accept(gen);
+               cons.getMethodInfo().setCodeAttribute(
+                                       bytecode.toCodeAttribute());
+               cons.setExceptionTypes(tlist);
+               return cons;
+           }
+           else {
+               Declarator r = md.getReturn();
+               CtClass rtype = gen.lookupClass(r);
+               recordReturnType(rtype, false);
+               CtMethod method = new CtMethod(rtype, r.getVariable().get(),
+                                          plist, gen.getThisClass());
+               method.setModifiers(mod);
+               gen.setThisMethod(method);
+               md.accept(gen);
+               if (md.getBody() != null)
+                   method.getMethodInfo().setCodeAttribute(
+                                       bytecode.toCodeAttribute());
+               else
+                   method.setModifiers(mod | Modifier.ABSTRACT);
+
+               method.setExceptionTypes(tlist);
+               return method;
+           }
+       }
+       catch (NotFoundException e) {
+           throw new CompileError(e.toString());
+       }
+    }
+
+    /**
+     * Compiles a method (or constructor) body.
+     */
+    public Bytecode compileBody(CtBehavior method, String src)
+       throws CompileError
+    {
+       try {
+           int mod = method.getModifiers();
+           recordParams(method.getParameterTypes(), Modifier.isStatic(mod));
+
+           CtClass rtype;
+           if (method instanceof CtMethod) {
+               gen.setThisMethod((CtMethod)method);
+               rtype = ((CtMethod)method).getReturnType();
+           }
+           else
+               rtype = CtClass.voidType;
+
+           recordReturnType(rtype, false);
+           boolean isVoid = rtype == CtClass.voidType;
+
+           Parser p = new Parser(new Lex(src));
+           SymbolTable stb = new SymbolTable(stable);
+           Stmnt s = p.parseStatement(stb);
+           gen.atMethodBody(s, method instanceof CtConstructor, isVoid);
+           return bytecode;
+       }
+       catch (NotFoundException e) {
+           throw new CompileError(e.toString());
+       }
+    }
+
+    /**
+     * Makes variables $0 (this), $1, $2, ..., and $args represent method
+     * parameters.  $args represents an array of all the parameters.
+     * It also makes $$ available as a parameter list of method call.
+     *
+     * <p>This must be called before calling <code>compileStmnt()</code> and
+     * <code>compileExpr()</code>.  The correct value of
+     * <code>isStatic</code> must be recorded before compilation.
+     */
+    public void recordParams(CtClass[] params, boolean isStatic)
+       throws CompileError
+    {
+       gen.recordParams(params, isStatic, "$", "$args", "$$", stable);
+    }
+
+    /**
+     * Makes variables $0, $1, $2, ..., and $args represent method
+     * parameters.  $args represents an array of all the parameters.
+     * It also makes $$ available as a parameter list of method call.
+     * $0 can represent a local variable other than THIS (variable 0).
+     *
+     * <p>This must be called before calling <code>compileStmnt()</code> and
+     * <code>compileExpr()</code>.  The correct value of
+     * <code>isStatic</code> must be recorded before compilation.
+     *
+     * @paaram use0    true if $0 is used.
+     * @param varNo    the register number of $0 (use0 is true)
+     *                         or $1 (otherwise).
+     * @param target   the type of $0 (it can be null if use0 is false).
+     * @param isStatic true if the method in which the compiled bytecode
+     *                 is embedded is static.
+     */
+    public void recordParams(String target, CtClass[] params,
+                            boolean use0, int varNo, boolean isStatic)
+       throws CompileError
+    {
+       gen.recordParams(params, isStatic, "$", "$args", "$$",
+                        use0, varNo, target, stable);
+    }
+
+    /**
+     * Prepares to use cast $r, $w, $_, and $type.
+     * It also enables to write a return statement with a return value
+     * for void method.
+     *
+     * <p>If the return type is void, ($r) does nothing.
+     * The type of $_ is java.lang.Object.
+     *
+     * @param useResultVar     true if $_ is used.
+     * @return         -1 or the variable index assigned to $_.
+     */
+    public int recordReturnType(CtClass type, boolean useResultVar)
+       throws CompileError
+    {
+       gen.recordType(type);
+       return gen.recordReturnType(type, "$r",
+                       (useResultVar ? resultVarName : null), stable);
+    }
+
+    /**
+     * Prepares to use $type.  Note that recordReturnType() overwrites
+     * the value of $type.
+     */
+    public void recordType(CtClass t) {
+       gen.recordType(t);
+    }
+
+    /**
+     * Makes the given variable available.
+     *
+     * @param type     variable type
+     * @param name     variable name
+     */
+    public int recordVariable(CtClass type, String name)
+       throws CompileError
+    {
+       return gen.recordVariable(type, name, stable);
+    }
+
+    /**
+     * Prepares to use $proceed().
+     * If the return type of $proceed() is void, null is pushed on the
+     * stack.
+     *
+     * @param target   an expression specifying the target object.
+     *                         if null, "this" is the target.
+     * @param method   the method name.
+     */
+    public void recordProceed(String target, String method)
+       throws CompileError
+    {
+       Parser p = new Parser(new Lex(target));
+       final ASTree texpr = p.parseExpression(stable);
+       final String m = method;
+
+       ProceedHandler h = new ProceedHandler() {
+               public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
+                   throws CompileError
+               {
+                   ASTree expr = new Member(m);
+                   if (texpr != null)
+                       expr = Expr.make('.', texpr, expr);
+
+                   expr = Expr.make(TokenId.CALL, expr, args);
+                   expr.accept(gen);
+                   gen.addNullIfVoid();
+               }
+           };
+
+       gen.setProceedHandler(h, proceedName);
+    }
+
+    /**
+     * Prepares to use $proceed().
+     */
+    public void recordProceed(ProceedHandler h) {
+       gen.setProceedHandler(h, proceedName);
+    }
+
+    /**
+     * Compiles a statement (or a block).
+     * <code>recordParams()</code> must be called before invoking
+     * this method.
+     *
+     * <p>Local variables that are not declared
+     * in the compiled source text are not accessible within that
+     * source text.  Fields and method parameters
+     * ($0, $1, ..) are available.
+     */
+    public void compileStmnt(String src) throws CompileError {
+       Parser p = new Parser(new Lex(src));
+       SymbolTable stb = new SymbolTable(stable);
+     // while (p.hasMore()) {
+           Stmnt s = p.parseStatement(stb);
+           if (s != null)
+               s.accept(gen);
+     // }
+    }
+
+    /**
+     * Compiles an exression.  <code>recordParams()</code> must be
+     * called before invoking this method.
+     *
+     * <p>Local variables are not accessible
+     * within the compiled source text.  Fields and method parameters
+     * ($0, $1, ..) are available if <code>recordParams()</code>
+     * have been invoked.
+     */
+    public void compileExpr(String src) throws CompileError {
+       Parser p = new Parser(new Lex(src));
+       ASTree e = p.parseExpression(stable);
+       compileExpr(e);
+    }
+
+    /**
+     * Compiles an exression.  <code>recordParams()</code> must be
+     * called before invoking this method.
+     *
+     * <p>Local variables are not accessible
+     * within the compiled source text.  Fields and method parameters
+     * ($0, $1, ..) are available if <code>recordParams()</code>
+     * have been invoked.
+     */
+    public void compileExpr(ASTree e) throws CompileError {
+       if (e != null)
+           e.accept(gen);
+    }
+}
diff --git a/src/main/javassist/compiler/JvstCodeGen.java b/src/main/javassist/compiler/JvstCodeGen.java
new file mode 100644 (file)
index 0000000..6a365e1
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.ast.*;
+
+/* Code generator methods for extensions by Javassist.
+ */
+public class JvstCodeGen extends MemberCodeGen {
+    private String paramArrayName = null;
+    private String paramListName = null;
+    private CtClass[] paramTypeList = null;
+    private int paramVarBase = 0;      // variable index for $0 or $1.
+    private boolean useParam0 = false; // true if $0 is used.
+    private String param0Type = null;  // JVM name
+    private static final String sigName = "$sig";
+    private static final String dollarTypeName = "$type";
+    private static final String clazzName = "$class";
+    private CtClass dollarType = null;
+    private CtClass returnType = null;
+    private String returnCastName = null;
+    private String returnVarName = null;       // null if $_ is not used.
+    private static final String wrapperCastName = "$w";
+    private String proceedName = null;
+    private static final String cflowName = "$cflow";
+    private ProceedHandler procHandler = null; // null if not used.
+
+    public JvstCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
+       super(b, cc, cp);
+    }
+
+    /* Index of $1.
+     */
+    private int indexOfParam1() {
+       return paramVarBase + (useParam0 ? 1 : 0);
+    }
+
+    /* Records a ProceedHandler obejct.
+     *
+     * @param name     the name of the special method call.
+     *                 it is usually $proceed.
+     */
+    public void setProceedHandler(ProceedHandler h, String name) {
+       proceedName = name;
+       procHandler = h;
+    }
+
+    /* If the type of the expression compiled last is void,
+     * add ACONST_NULL and change exprType, arrayDim, className.
+     */
+    public void addNullIfVoid() {
+       if (exprType == VOID) {
+           bytecode.addOpcode(ACONST_NULL);
+           exprType = CLASS;
+           arrayDim = 0;
+           className = jvmJavaLangObject;
+       }
+    }
+
+    /* To support $args, $sig, and $type.
+     * $args is an array of parameter list.
+     */
+    public void atMember(Member mem) throws CompileError {
+       String name = mem.get();
+       if (name.equals(paramArrayName)) {
+           compileParameterList(bytecode, paramTypeList, indexOfParam1());
+           exprType = CLASS;
+           arrayDim = 1;
+           className = jvmJavaLangObject;
+       }
+       else if (name.equals(sigName)) {
+           bytecode.addLdc(Descriptor.ofMethod(returnType, paramTypeList));
+           bytecode.addInvokestatic("javassist/runtime/Desc", "getParams",
+                               "(Ljava/lang/String;)[Ljava/lang/Class;");
+           exprType = CLASS;
+           arrayDim = 1;
+           className = "java/lang/Class";
+       }
+       else if (name.equals(dollarTypeName)) {
+           if (dollarType == null)
+               throw new CompileError(dollarType + " is not available");
+
+           bytecode.addLdc(Descriptor.of(dollarType));
+           callGetType("getType");
+       }
+       else if (name.equals(clazzName)) {
+           if (param0Type == null)
+               throw new CompileError(clazzName + " is not available");
+
+           bytecode.addLdc(param0Type);
+           callGetType("getClazz");
+       }
+       else
+           super.atMember(mem);
+    }
+
+    private void callGetType(String method) {
+       bytecode.addInvokestatic("javassist/runtime/Desc", method,
+                               "(Ljava/lang/String;)Ljava/lang/Class;");
+       exprType = CLASS;
+       arrayDim = 0;
+       className = "java/lang/Class";
+    }
+
+    private void atSigOrType(String sig) throws CompileError {
+    }
+
+    protected void atFieldAssign(Expr expr, int op, ASTree left,
+                       ASTree right, boolean doDup) throws CompileError
+    {
+       if (left instanceof Member
+           && ((Member)left).get().equals(paramArrayName)) {
+           if (op != '=')
+               throw new CompileError("bad operator for " + paramArrayName);
+
+           right.accept(this);
+           if (arrayDim != 1 || exprType != CLASS)
+               throw new CompileError("invalid type for " + paramArrayName);
+
+           atAssignParamList(paramTypeList, bytecode);
+           if (!doDup)
+               bytecode.addOpcode(POP);
+       }
+       else
+           super.atFieldAssign(expr, op, left, right, doDup);
+    }
+
+    protected void atAssignParamList(CtClass[] params, Bytecode code)
+       throws CompileError
+    {
+       if (params == null)
+           return;
+
+       int varNo = indexOfParam1();
+       int n = params.length;
+       for (int i = 0; i < n; ++i) {
+           code.addOpcode(DUP);
+           code.addIconst(i);
+           code.addOpcode(AALOAD);
+           compileUnwrapValue(params[i], code);
+           code.addStore(varNo, params[i]);
+           varNo += is2word(exprType, arrayDim) ? 2 : 1;
+       }
+    }
+
+    public void atCastExpr(CastExpr expr) throws CompileError {
+       ASTList classname = expr.getClassName();
+       if (classname != null && expr.getArrayDim() == 0) {
+           ASTree p = classname.head();
+           if (p instanceof Symbol && classname.tail() == null) {
+               String typename = ((Symbol)p).get();
+               if (typename.equals(returnCastName)) {
+                   atCastToRtype(expr);
+                   return;
+               }
+               else if (typename.equals(wrapperCastName)) {
+                   atCastToWrapper(expr);
+                   return;
+               }
+           }
+       }
+
+       super.atCastExpr(expr);
+    }
+
+    /**
+     * Inserts a cast operator to the return type.
+     * If the return type is void, this does nothing.
+     */
+    protected void atCastToRtype(CastExpr expr) throws CompileError {
+       expr.getOprand().accept(this);
+       if (!isRefType(exprType) || arrayDim > 0)
+           throw new CompileError("invalid type for " + returnCastName);
+
+       compileUnwrapValue(returnType, bytecode);
+    }
+
+    protected void atCastToWrapper(CastExpr expr) throws CompileError {
+       expr.getOprand().accept(this);
+       if (isRefType(exprType) || arrayDim > 0)
+           return;     // Object type.  do nothing.
+
+       CtClass clazz = lookupClass(exprType, arrayDim, className);
+       if (clazz instanceof CtPrimitiveType) {
+           CtPrimitiveType pt = (CtPrimitiveType)clazz;
+           String wrapper = pt.getWrapperName();
+           bytecode.addNew(wrapper);           // new <wrapper>
+           bytecode.addOpcode(DUP);            // dup
+           if (pt.getDataSize() > 1)
+               bytecode.addOpcode(DUP2_X2);    // dup2_x2
+           else
+               bytecode.addOpcode(DUP2_X1);    // dup2_x1
+
+           bytecode.addOpcode(POP2);           // pop2
+           bytecode.addInvokespecial(wrapper, "<init>",
+                                     "(" + pt.getDescriptor() + ")V");
+                                               // invokespecial
+           exprType = CLASS;
+           arrayDim = 0;
+           className = jvmJavaLangObject;
+       }
+    }
+
+    /* Delegates to a ProcHandler object if the method call is
+     * $proceed().  It may process $cflow().
+     */
+    protected void atMethodCall(Expr expr) throws CompileError {
+       ASTree method = expr.oprand1();
+       if (method instanceof Member) {
+           String name = ((Member)method).get();
+           if (procHandler != null && name.equals(proceedName)) {
+               procHandler.doit(this, bytecode, (ASTList)expr.oprand2());
+               return;
+           }
+           else if (name.equals(cflowName)) {
+               atCflow((ASTList)expr.oprand2());
+               return;
+           }
+       }
+
+       super.atMethodCall(expr);
+    }
+
+    /* To support $cflow().
+     */
+    protected void atCflow(ASTList cname) throws CompileError {
+       StringBuffer sbuf = new StringBuffer();
+       if (cname == null || cname.tail() != null)
+           throw new CompileError("bad " + cflowName);
+
+       makeCflowName(sbuf, cname.head());
+       String name = sbuf.toString();
+       Object[] names = classPool.lookupCflow(name);
+       if (names == null)
+           throw new CompileError("no such a " + cflowName + ": " + name);
+
+       bytecode.addGetstatic((String)names[0], (String)names[1],
+                             "Ljavassist/runtime/Cflow;");
+       bytecode.addInvokevirtual("javassist.runtime.Cflow",
+                                 "value", "()I");
+       exprType = INT;
+       arrayDim = 0;
+       className = null;
+    }
+
+    /* Syntax:
+     *
+     * <cflow> : $cflow '(' <cflow name> ')'
+     * <cflow name> : <identifier> ('.' <identifier>)*
+     */
+    private static void makeCflowName(StringBuffer sbuf, ASTree name)
+       throws CompileError
+    {
+       if (name instanceof Symbol) {
+           sbuf.append(((Symbol)name).get());
+           return;
+       }
+       else if (name instanceof Expr) {
+           Expr expr = (Expr)name;
+           if (expr.getOperator() == '.') {
+               makeCflowName(sbuf, expr.oprand1());
+               sbuf.append('.');
+               makeCflowName(sbuf, expr.oprand2());
+               return;
+           }
+       }
+
+       throw new CompileError("bad " + cflowName);
+    }
+
+    /* To support $$.  ($$) is equivalent to ($1, ..., $n).
+     * It can be used only as a parameter list of method call.
+     */
+    public boolean isParamListName(ASTList args) {
+       if (paramTypeList != null
+           && args != null && args.tail() == null) {
+           ASTree left = args.head();
+           return (left instanceof Member
+                   && ((Member)left).get().equals(paramListName));
+       }
+       else
+           return false;
+    }
+
+    /*
+    public int atMethodArgsLength(ASTList args) {
+       if (!isParamListName(args))
+           return super.atMethodArgsLength(args);
+
+       return paramTypeList.length;
+    }
+    */
+
+    public int atMethodArgsLength(ASTList args) {
+       String pname = paramListName;
+       int n = 0;
+       while (args != null) {
+           ASTree a = args.head();
+           if (a instanceof Member && ((Member)a).get().equals(pname)) {
+               if (paramTypeList != null)
+                   n += paramTypeList.length;
+           }
+           else
+               ++n;
+
+           args = args.tail();
+       }
+
+       return n;
+    }
+
+    public void atMethodArgs(ASTList args, int[] types, int[] dims,
+                               String[] cnames) throws CompileError {
+       CtClass[] params = paramTypeList;
+       String pname = paramListName;
+       int i = 0;
+       while (args != null) {
+           ASTree a = args.head();
+           if (a instanceof Member && ((Member)a).get().equals(pname)) {
+               if (params != null) {
+                   int n = params.length;
+                   int regno = indexOfParam1();
+                   for (int k = 0; k < n; ++k) {
+                       CtClass p = params[k];
+                       regno += bytecode.addLoad(regno, p);
+                       setType(p);
+                       types[i] = exprType;
+                       dims[i] = arrayDim;
+                       cnames[i] = className;
+                       ++i;
+                   }
+               }
+           }
+           else {
+               a.accept(this);
+               types[i] = exprType;
+               dims[i] = arrayDim;
+               cnames[i] = className;
+               ++i;
+           }
+
+           args = args.tail();
+       }
+    }
+
+    /*
+    public void atMethodArgs(ASTList args, int[] types, int[] dims,
+                               String[] cnames) throws CompileError {
+       if (!isParamListName(args)) {
+           super.atMethodArgs(args, types, dims, cnames);
+           return;
+       }
+
+       CtClass[] params = paramTypeList;
+       if (params == null)
+           return;
+
+       int n = params.length;
+       int regno = indexOfParam1();
+       for (int i = 0; i < n; ++i) {
+           CtClass p = params[i];
+           regno += bytecode.addLoad(regno, p);
+           setType(p);
+           types[i] = exprType;
+           dims[i] = arrayDim;
+           cnames[i] = className;
+       }
+    }
+    */
+
+    /*
+     * Makes it valid to write "return <expr>;" for a void method.
+     */
+    protected void atReturnStmnt(Stmnt st) throws CompileError {
+       ASTree result = st.getLeft();
+       if (result != null && returnType == CtClass.voidType) {
+           result.accept(this);
+           if (is2word(exprType, arrayDim))
+               bytecode.addOpcode(POP2);
+           else if (exprType != VOID)
+               bytecode.addOpcode(POP);
+
+           result = null;
+       }
+
+       atReturnStmnt2(result);
+    }
+
+    /**
+     * Makes a cast to the return type ($r) available.
+     * It also enables $_.
+     *
+     * <p>If the return type is void, ($r) does nothing.
+     * The type of $_ is java.lang.Object.
+     *
+     * @param resultName       null if $_ is not used.
+     * @return         -1 or the variable index assigned to $_.
+     */
+    public int recordReturnType(CtClass type, String castName,
+                String resultName, SymbolTable tbl) throws CompileError
+    {
+       returnType = type;
+       returnCastName = castName;
+       returnVarName = resultName;
+       if (resultName == null)
+           return -1;
+       else {
+           int varNo = getMaxLocals();
+           int locals = varNo + recordVar(type, resultName, varNo, tbl);
+           setMaxLocals(locals);
+           return varNo;
+       }
+    }
+
+    /**
+     * Makes $type available.
+     */
+    public void recordType(CtClass t) {
+       dollarType = t;
+    }
+
+    /**
+     * Makes method parameters $0, $1, ..., $args, and $$ available.
+     * $0 is equivalent to THIS if the method is not static.  Otherwise,
+     * if the method is static, then $0 is not available.
+     */
+    public void recordParams(CtClass[] params, boolean isStatic,
+                            String prefix, String paramVarName,
+                            String paramsName, SymbolTable tbl)
+       throws CompileError
+    {
+       recordParams(params, isStatic, prefix, paramVarName,
+                    paramsName, !isStatic, 0, getThisName(), tbl);
+    }
+
+    /**
+     * Makes method parameters $0, $1, ..., $args, and $$ available.
+     * $0 is available only if use0 is true.  It might not be equivalent
+     * to THIS.
+     *
+     * @paaram use0    true if $0 is used.
+     * @param paramBase        the register number of $0 (use0 is true)
+     *                         or $1 (otherwise).
+     * @param target   the class of $0.  If use0 is false, target
+     *                 can be null.
+     * @param isStatic true if the method in which the compiled bytecode
+     *                 is embedded is static.
+     */
+    public void recordParams(CtClass[] params, boolean isStatic,
+                            String prefix, String paramVarName,
+                            String paramsName, boolean use0,
+                            int paramBase, String target,
+                            SymbolTable tbl)
+       throws CompileError
+    {
+       int varNo;
+
+       paramTypeList = params;
+       paramArrayName = paramVarName;
+       paramListName = paramsName;
+       paramVarBase = paramBase;
+       useParam0 = use0;
+
+       param0Type = jvmToJavaName(target);
+
+       inStaticMethod = isStatic;
+       varNo = paramBase;
+       if (use0) {
+           String varName = prefix + "0";
+           Declarator decl
+               = new Declarator(CLASS, javaToJvmName(target), 0, varNo++,
+                                new Symbol(varName));
+           tbl.append(varName, decl);
+       }
+
+       for (int i = 0; i < params.length; ++i)
+           varNo += recordVar(params[i], prefix + (i + 1), varNo, tbl);
+
+       if (getMaxLocals() < varNo)
+           setMaxLocals(varNo);
+    }
+
+    /**
+     * Makes the given variable name available.
+     *
+     * @param type     variable type
+     * @param varName  variable name
+     */
+    public int recordVariable(CtClass type, String varName, SymbolTable tbl)
+       throws CompileError
+    {
+       if (varName == null)
+           return -1;
+       else {
+           int varNo = getMaxLocals();
+           int locals = varNo + recordVar(type, varName, varNo, tbl);
+           setMaxLocals(locals);
+           return varNo;
+       }
+    }
+
+    private int recordVar(CtClass cc, String varName, int varNo,
+                         SymbolTable tbl) throws CompileError
+    {
+       if (cc == CtClass.voidType) {
+           exprType = CLASS;
+           arrayDim = 0;
+           className = jvmJavaLangObject;
+       }
+       else
+           setType(cc);
+
+       Declarator decl
+           = new Declarator(exprType, className, arrayDim,
+                            varNo, new Symbol(varName));
+       tbl.append(varName, decl);
+       return is2word(exprType, arrayDim) ? 2 : 1;
+    }
+
+    /* compileParameterList() returns the stack size used
+     * by the produced code.
+     *
+     * This method correctly computes the max_stack value.
+     *
+     * @param regno    the index of the local variable in which
+     *                 the first argument is received.
+     *                 (0: static method, 1: regular method.)
+     */
+    public static int compileParameterList(Bytecode code,
+                               CtClass[] params, int regno) {
+       if (params == null) {
+           code.addIconst(0);                          // iconst_0
+           code.addAnewarray(javaLangObject);          // anewarray Object
+           return 1;
+       }
+       else {
+           CtClass[] args = new CtClass[1];
+           int n = params.length;
+           code.addIconst(n);                          // iconst_<n>
+           code.addAnewarray(javaLangObject);          // anewarray Object
+           for (int i = 0; i < n; ++i) {
+               code.addOpcode(Bytecode.DUP);           // dup
+               code.addIconst(i);                      // iconst_<i>
+               if (params[i].isPrimitive()) {
+                   CtPrimitiveType pt = (CtPrimitiveType)params[i];
+                   String wrapper = pt.getWrapperName();
+                   code.addNew(wrapper);               // new <wrapper>
+                   code.addOpcode(Bytecode.DUP);       // dup
+                   int s = code.addLoad(regno, pt);    // ?load <regno>
+                   regno += s;
+                   args[0] = pt;
+                   code.addInvokespecial(wrapper, "<init>",
+                               Descriptor.ofMethod(CtClass.voidType, args));
+                                                       // invokespecial
+               }
+               else {
+                   code.addAload(regno);               // aload <regno>
+                   ++regno;
+               }
+
+               code.addOpcode(Bytecode.AASTORE);       // aastore
+           }
+
+           return 8;
+       }
+    }
+
+    protected void compileUnwrapValue(CtClass type, Bytecode code)
+       throws CompileError
+    {
+       if (type instanceof CtPrimitiveType) {
+           CtPrimitiveType pt = (CtPrimitiveType)type;
+           if (pt != CtClass.voidType) {
+               String wrapper = pt.getWrapperName();
+               code.addCheckcast(wrapper);
+               code.addInvokevirtual(wrapper, pt.getGetMethodName(),
+                                     pt.getGetMethodDescriptor());
+               setType(type);
+           }
+       }
+       else {
+           code.addCheckcast(type);
+           setType(type);
+       }
+    }
+
+    /* Sets exprType, arrayDim, and className;
+     * If type is void, then this method does nothing.
+     */
+    public void setType(CtClass type) throws CompileError {
+       setType(type, 0);
+    }
+
+    private void setType(CtClass type, int dim) throws CompileError {
+       if (type.isPrimitive()) {
+           CtPrimitiveType pt = (CtPrimitiveType)type;
+           exprType = descToType(pt.getDescriptor());
+           arrayDim = dim;
+           className = null;
+       }
+       else if (type.isArray())
+           try {
+               setType(type.getComponentType(), dim + 1);
+           }
+           catch (NotFoundException e) {
+               throw new CompileError("undefined type: " + type.getName());
+           }
+       else {
+           exprType = CLASS;
+           arrayDim = dim;
+           className = javaToJvmName(type.getName());
+       }
+    }
+
+    /* Performs implicit coercion from exprType to type.
+     */
+    public void doNumCast(CtClass type) throws CompileError {
+       if (arrayDim == 0 && !isRefType(exprType))
+           if (type instanceof CtPrimitiveType) {
+               CtPrimitiveType pt = (CtPrimitiveType)type;
+               atNumCastExpr(exprType, descToType(pt.getDescriptor()));
+           }
+           else
+               throw new CompileError("type mismatch");
+    }
+}
diff --git a/src/main/javassist/compiler/KeywordTable.java b/src/main/javassist/compiler/KeywordTable.java
new file mode 100644 (file)
index 0000000..57145bf
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+final class KeywordTable extends java.util.HashMap {
+    public KeywordTable() { super(); }
+
+    public int lookup(String name) {
+       Object found = get(name);
+       if (found == null)
+           return -1;
+       else
+           return ((Integer)found).intValue();
+    }
+
+    public void append(String name, int t) {
+       put(name, new Integer(t));
+    }
+}
diff --git a/src/main/javassist/compiler/Lex.java b/src/main/javassist/compiler/Lex.java
new file mode 100644 (file)
index 0000000..fb188de
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+class Token {
+    public Token next = null;
+    public int tokenId;
+
+    public long longValue;
+    public double doubleValue;
+    public String textValue;
+}
+
+public class Lex implements TokenId {
+    private int lastChar;
+    private StringBuffer textBuffer;
+    private Token currentToken;
+    private Token lookAheadTokens;
+
+    private String input;
+    private int position, maxlen, lineNumber;
+
+    /**
+     * Constructs a lexical analyzer.
+     */
+    public Lex(String s) {
+       lastChar = -1;
+       textBuffer = new StringBuffer();
+       currentToken = new Token();
+       lookAheadTokens = null;
+
+       input = s;
+       position = 0;
+       maxlen = s.length();
+       lineNumber = 0;
+    }
+
+    public int get() {
+       if (lookAheadTokens == null)
+           return get(currentToken);
+       else {
+           Token t;
+           currentToken = t = lookAheadTokens;
+           lookAheadTokens = lookAheadTokens.next;
+           return t.tokenId;
+       }
+    }
+
+    /**
+     * Looks at the next token.
+     */
+    public int lookAhead() {
+       return lookAhead(0);
+    }
+
+    public int lookAhead(int i) {
+       Token tk = lookAheadTokens;
+       if (tk == null) {
+           lookAheadTokens = tk = currentToken;  // reuse an object!
+           tk.next = null;
+           get(tk);
+       }
+
+       for (; i-- > 0; tk = tk.next)
+           if (tk.next == null) {
+               Token tk2;
+               tk.next = tk2 = new Token();
+               get(tk2);
+           }
+
+       currentToken = tk;
+       return tk.tokenId;
+    }
+
+    public String getString() {
+       return currentToken.textValue;
+    }
+
+    public long getLong() {
+       return currentToken.longValue;
+    }
+
+    public double getDouble() {
+       return currentToken.doubleValue;
+    }
+
+    private int get(Token token) {
+       int t;
+       do {
+           t = readLine(token);
+       } while (t == '\n');
+       token.tokenId = t;
+       return t;
+    }
+
+    private int readLine(Token token) {
+       int c = getNextNonWhiteChar();
+       if(c < 0)
+           return c;
+       else if(c == '\n') {
+           ++lineNumber;
+           return '\n';
+       }
+       else if (c == '\'')
+           return readCharConst(token);
+       else if (c == '"')
+           return readStringL(token);
+       else if ('0' <= c && c <= '9')
+           return readNumber(c, token);
+       else if(c == '.'){
+           c = getc();
+           if ('0' <= c && c <= '9') {
+               StringBuffer tbuf = textBuffer;
+               tbuf.setLength(0);
+               tbuf.append('.');
+               return readDouble(tbuf, c, token);
+           }
+           else{
+               ungetc(c);
+               return readSeparator('.');
+           }
+       }
+       else if ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '_'
+                || c == '$')
+           return readIdentifier(c, token);
+       else
+           return readSeparator(c);
+    }
+
+    private int getNextNonWhiteChar() {
+       int c;
+       do {
+           c = getc();
+           if (c == '/') {
+               c = getc();
+               if (c == '/')
+                   do {
+                       c = getc();
+                   } while (c != '\n' && c != '\r' && c != -1);
+               else if (c == '*')
+                   while (true) {
+                       c = getc();
+                       if (c == -1)
+                           break;
+                       else if (c == '*')
+                           if ((c = getc()) == '/') {
+                               c = ' ';
+                               break;
+                           }
+                           else
+                               ungetc(c);
+                   }
+               else {
+                   ungetc(c);
+                   c = '/';
+               }
+           }
+       } while(isBlank(c));
+       return c;
+    }
+
+    private int readCharConst(Token token) {
+       int c;
+       int value = 0;
+       while ((c = getc()) != '\'')
+           if (c == '\\')
+               value = readEscapeChar();
+           else if (c < 0x20) {
+               if (c == '\n')
+                   ++lineNumber;
+
+               return BadToken;
+           }
+           else
+               value = c;
+
+       token.longValue = value;
+       return CharConstant;
+    }
+
+    private int readEscapeChar() {
+       int c = getc();
+       if (c == 'n')
+           c = '\n';
+       else if (c == 't')
+           c = '\t';
+       else if (c == 'r')
+           c = '\r';
+       else if (c == 'f')
+           c = '\f';
+       else if (c == '\n')
+           ++lineNumber;
+
+       return c;
+    }
+
+    private int readStringL(Token token) {
+       int c;
+       StringBuffer tbuf = textBuffer;
+       tbuf.setLength(0);
+       for (;;) {
+           while ((c = getc()) != '"') {
+               if (c == '\\')
+                   c = readEscapeChar();
+               else if (c == '\n' || c < 0) {
+                   ++lineNumber;
+                   return BadToken;
+               }
+
+               tbuf.append((char)c);
+           }
+
+           for (;;) {
+               c = getc();
+               if (c == '\n')
+                   ++lineNumber;
+               else if (!isBlank(c))
+                   break;
+           }
+
+           if (c != '"') {
+               ungetc(c);
+               break;
+           }
+       }
+
+       token.textValue = tbuf.toString();
+       return StringL;
+    }
+
+    private int readNumber(int c, Token token) {
+       long value = 0;
+       int c2 = getc();
+       if (c == '0')
+           if (c2 == 'X' || c2 == 'x')
+               for (;;) {
+                   c = getc();
+                   if ('0' <= c && c <= '9')
+                       value = value * 16 + (long)(c - '0');
+                   else if ('A' <= c && c <= 'F')
+                       value = value * 16 + (long)(c - 'A' + 10);
+                   else if ('a' <= c && c <= 'f')
+                       value = value * 16 + (long)(c - 'a' + 10);
+                   else {
+                       token.longValue = value;
+                       if (c == 'L' || c == 'l')
+                           return LongConstant;
+                       else {
+                           ungetc(c);
+                           return IntConstant;
+                       }
+                   }
+               }
+           else if ('0' <= c2 && c2 <= '7') {
+               value = c2 - '0';
+               for (;;) {
+                   c = getc();
+                   if ('0' <= c && c <= '7')
+                       value = value * 8 + (long)(c - '0');
+                   else {
+                       token.longValue = value;
+                       if (c == 'L' || c == 'l')
+                           return LongConstant;
+                       else {
+                           ungetc(c);
+                           return IntConstant;
+                       }
+                   }
+               }
+           }
+
+       value = c - '0';
+       while ('0' <= c2 && c2 <= '9') {
+           value = value * 10 + c2 - '0';
+           c2 = getc();
+       }
+
+       token.longValue = value;
+       if (c2 == 'F' || c2 == 'f') {
+           token.doubleValue = (double)value;
+           return FloatConstant;
+       }
+       else if (c2 == 'E' || c2 == 'e' || c2 == '.') {
+           StringBuffer tbuf = textBuffer;
+           tbuf.setLength(0);
+           tbuf.append(value);
+           return readDouble(tbuf, c2, token);
+       }
+       else if (c2 == 'L' || c2 == 'l')
+           return LongConstant;
+       else {
+           ungetc(c2);
+           return IntConstant;
+       }
+    }
+
+    private int readDouble(StringBuffer sbuf, int c, Token token) {
+       if (c != 'E' && c != 'e') {
+           sbuf.append((char)c);
+           for (;;) {
+               c = getc();
+               if ('0' <= c && c <= '9')
+                   sbuf.append((char)c);
+               else
+                   break;
+           }
+       }
+
+       if (c == 'E' || c == 'e') {
+           sbuf.append((char)c);
+           c = getc();
+           if (c == '+' || c == '-') {
+               sbuf.append((char)c);
+               c = getc();
+           }
+
+           while ('0' <= c && c <= '9') {
+               sbuf.append((char)c);
+               c = getc();
+           }
+       }
+
+       try {
+           token.doubleValue = Double.parseDouble(sbuf.toString());
+       }
+       catch (NumberFormatException e) {
+           return BadToken;
+       }
+
+       if (c == 'F' || c == 'f')
+           return FloatConstant;
+       else {
+           ungetc(c);
+           return DoubleConstant;
+       }
+    }
+
+    // !"#$%&'(    )*+,-./0    12345678    9:;<=>?
+    private static final int[] equalOps
+       =  { NEQ, 0, 0, 0, MOD_E, AND_E, 0, 0,
+            0, MUL_E, PLUS_E, 0, MINUS_E, 0, DIV_E, 0,
+            0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, LE, EQ, GE, 0 };
+
+    private int readSeparator(int c) {
+       int c2, c3;
+       if ('!' <= c && c <= '?') {
+           int t = equalOps[c - '!'];
+           if (t == 0) 
+               return c;
+           else {
+               c2 = getc();
+               if (c == c2)
+                   switch (c) {
+                   case '=' :
+                       return EQ;
+                   case '+' :
+                       return PLUSPLUS;
+                   case '-' :
+                       return MINUSMINUS;
+                   case '&' :
+                       return ANDAND;
+                   case '<' :
+                       c3 = getc();
+                       if (c3 == '=')
+                           return LSHIFT_E;
+                       else {
+                           ungetc(c3);
+                           return LSHIFT;
+                       }
+                   case '>' :
+                       c3 = getc();
+                       if (c3 == '=')
+                           return RSHIFT_E;
+                       else if (c3 == '>') {
+                           c3 = getc();
+                           if (c3 == '=')
+                               return ARSHIFT_E;
+                           else {
+                               ungetc(c3);
+                               return ARSHIFT;
+                           }
+                       }
+                       else {
+                           ungetc(c3);
+                           return RSHIFT;
+                       }
+                   default :
+                       break;
+                   }
+               else if (c2 == '=')
+                   return t;
+           }
+       }
+       else if (c == '^') {
+           c2 = getc();
+           if (c2 == '=')
+               return EXOR_E;
+       }
+       else if (c == '|') {
+           c2 = getc();
+           if (c2 == '=')
+               return OR_E;
+           else if (c2 == '|')
+               return OROR;
+       }
+       else
+           return c;
+
+       ungetc(c2);
+       return c;
+    }
+
+    private int readIdentifier(int c, Token token) {
+       StringBuffer tbuf = textBuffer;
+       tbuf.setLength(0);
+
+       do {
+           tbuf.append((char)c);
+           c = getc();
+       } while ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '_'
+                || c == '$' || '0' <= c && c <= '9');
+
+       ungetc(c);
+
+       String name = tbuf.toString();
+       int t = ktable.lookup(name);
+       if (t >= 0)
+           return t;
+       else {
+           /* tbuf.toString() is executed quickly since it does not
+            * need memory copy.  Using a hand-written extensible
+            * byte-array class instead of StringBuffer is not a good idea
+            * for execution speed.  Converting a byte array to a String
+            * object is very slow.  Using an extensible char array
+            * might be OK.
+            */
+           token.textValue = name;
+           return Identifier;
+       }
+    }
+
+    private static final KeywordTable ktable = new KeywordTable();
+
+    static {
+       ktable.append("abstract", ABSTRACT);
+       ktable.append("boolean", BOOLEAN);
+       ktable.append("break", BREAK);
+       ktable.append("byte", BYTE);
+       ktable.append("case", CASE);
+       ktable.append("catch", CATCH);
+       ktable.append("char", CHAR);
+       ktable.append("class", CLASS);
+       ktable.append("const", CONST);
+       ktable.append("continue", CONTINUE);
+       ktable.append("default", DEFAULT);
+       ktable.append("do", DO);
+       ktable.append("double", DOUBLE);
+       ktable.append("else", ELSE);
+       ktable.append("extends", EXTENDS);
+       ktable.append("false", FALSE);
+       ktable.append("final", FINAL);
+       ktable.append("finally", FINALLY);
+       ktable.append("float", FLOAT);
+       ktable.append("for", FOR);
+       ktable.append("goto", GOTO);
+       ktable.append("if", IF);
+       ktable.append("implements", IMPLEMENTS);
+       ktable.append("import", IMPORT);
+       ktable.append("instanceof", INSTANCEOF);
+       ktable.append("int", INT);
+       ktable.append("interface", INTERFACE);
+       ktable.append("long", LONG);
+       ktable.append("native", NATIVE);
+       ktable.append("new", NEW);
+       ktable.append("null", NULL);
+       ktable.append("package", PACKAGE);
+       ktable.append("private", PRIVATE);
+       ktable.append("protected", PROTECTED);
+       ktable.append("public", PUBLIC);
+       ktable.append("return", RETURN);
+       ktable.append("short", SHORT);
+       ktable.append("static", STATIC);
+       ktable.append("strict", STRICT);
+       ktable.append("super", SUPER);
+       ktable.append("switch", SWITCH);
+       ktable.append("synchronized", SYNCHRONIZED);
+       ktable.append("this", THIS);
+       ktable.append("throw", THROW);
+       ktable.append("throws", THROWS);
+       ktable.append("transient", TRANSIENT);
+       ktable.append("true", TRUE);
+       ktable.append("try", TRY);
+       ktable.append("void", VOID);
+       ktable.append("volatile", VOLATILE);
+       ktable.append("while", WHILE);
+    }
+
+    private static boolean isBlank(int c) {
+       return c == ' ' || c == '\t' || c == '\f' || c == '\r'
+           || c == '\n';
+    }
+
+    private static boolean isDigit(int c) {
+       return '0' <= c && c <= '9';
+    }
+
+    private void ungetc(int c) {
+       lastChar = c;
+    }
+
+    private int getc() {
+       if (lastChar < 0)
+           if (position < maxlen)
+               return input.charAt(position++);
+           else
+               return -1;
+       else {
+           int c = lastChar;
+           lastChar = -1;
+           return c;
+       }
+    }
+}
diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java
new file mode 100644 (file)
index 0000000..d457a83
--- /dev/null
@@ -0,0 +1,1045 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+import java.util.List;
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.ast.*;
+
+/* Code generator methods depending on javassist.* classes.
+ */
+public class MemberCodeGen extends CodeGen {
+    protected ClassPool classPool;
+    protected CtClass   thisClass;
+    protected MethodInfo thisMethod;
+
+    protected boolean resultStatic;
+
+    public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
+       super(b);
+       classPool = cp;
+       thisClass = cc;
+       thisMethod = null;
+    }
+
+    /**
+     * Records the currently compiled method.
+     */
+    public void setThisMethod(CtMethod m) {
+       thisMethod = m.getMethodInfo2();
+    }
+
+    public CtClass getThisClass() { return thisClass; }
+
+    /**
+     * Returns the JVM-internal representation of this class name.
+     */
+    protected String getThisName() {
+       return javaToJvmName(thisClass.getName());
+    }
+
+    /**
+     * Returns the JVM-internal representation of this super class name.
+     */
+    protected String getSuperName() throws CompileError {
+       return javaToJvmName(getSuperclass(thisClass).getName());
+    }
+
+    protected void insertDefaultSuperCall() throws CompileError {
+       bytecode.addAload(0);
+       bytecode.addInvokespecial(getSuperclass(thisClass), "<init>", "()V");
+    }
+
+    protected void atTryStmnt(Stmnt st) throws CompileError {
+       Stmnt body = (Stmnt)st.getLeft();
+       if (body == null)
+           return;
+
+       int start = bytecode.currentPc();
+       body.accept(this);
+       int end = bytecode.currentPc();
+
+       bytecode.addOpcode(Opcode.GOTO);
+       int pc = bytecode.currentPc();
+       bytecode.addIndex(0);   // correct later
+
+       int var = getMaxLocals();
+       incMaxLocals(1);
+       ASTList catchList = (ASTList)st.getRight().getLeft();
+       while (catchList != null) {
+           Pair p = (Pair)catchList.head();
+           catchList = catchList.tail();
+           Declarator decl = (Declarator)p.getLeft();
+           Stmnt block = (Stmnt)p.getRight();
+
+           decl.setLocalVar(var);
+
+           CtClass type = lookupClass(decl.getClassName());
+           decl.setClassName(javaToJvmName(type.getName()));
+           bytecode.addExceptionHandler(start, end, bytecode.currentPc(),
+                                        type);
+           if (block != null) {
+               bytecode.addAstore(var);
+               block.accept(this);
+           }
+
+           bytecode.addOpcode(Opcode.GOTO);
+           bytecode.addIndex(pc - bytecode.currentPc());
+       }
+
+       Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft();
+       if (finallyBlock != null)
+           throw new CompileError(
+                       "sorry, finally has not been supported yet");
+
+       bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
+    }
+
+    public void atNewExpr(NewExpr expr) throws CompileError {
+       if (expr.isArray())
+           atNewArrayExpr(expr);
+       else {
+           CtClass clazz = lookupClass(expr.getClassName());
+           String cname = clazz.getName();
+           ASTList args = expr.getArguments();
+           bytecode.addNew(cname);
+           bytecode.addOpcode(DUP);
+
+           atMethodCall2(clazz, MethodInfo.nameInit, args, false, true);
+
+           exprType = CLASS;
+           arrayDim = 0;
+           className = javaToJvmName(cname);
+       }
+    }
+
+    public void atNewArrayExpr(NewExpr expr) throws CompileError {
+       if (expr.getInitializer() != null)
+           throw new CompileError("array initializer is not supported");
+
+       int type = expr.getArrayType();
+       ASTList size = expr.getArraySize();
+       ASTList classname = expr.getClassName();
+       if (size.length() > 1) {
+           atMultiNewArray(type, classname, size);
+           return;
+       }
+
+       size.head().accept(this);
+       exprType = type;
+       arrayDim = 1;
+       if (type == CLASS) {
+           className = resolveClassName(classname);
+           bytecode.addAnewarray(jvmToJavaName(className));
+       }
+       else {
+           className = null;
+           int atype = 0;
+           switch (type) {
+           case BOOLEAN :
+               atype = T_BOOLEAN;
+               break;
+           case CHAR :
+               atype = T_CHAR;
+               break;
+           case FLOAT :
+               atype = T_FLOAT;
+               break;
+           case DOUBLE :
+               atype = T_DOUBLE;
+               break;
+           case BYTE :
+               atype = T_BYTE;
+               break;
+           case SHORT :
+               atype = T_SHORT;
+               break;
+           case INT :
+               atype = T_INT;
+               break;
+           case LONG :
+               atype = T_LONG;
+               break;
+           default :
+               badNewExpr();
+               break;
+           }
+
+           bytecode.addOpcode(NEWARRAY);
+           bytecode.add(atype);
+       }
+    }
+
+    private static void badNewExpr() throws CompileError {
+       throw new CompileError("bad new expression");
+    }
+
+    protected void atMultiNewArray(int type, ASTList classname, ASTList size)
+       throws CompileError
+    {
+       int count, dim;
+       dim = size.length();
+       for (count = 0; size != null; size = size.tail()) {
+           ASTree s = size.head();
+           if (s == null)
+               break;          // int[][][] a = new int[3][4][];
+
+           ++count;
+           s.accept(this);
+           if (exprType != INT)
+               throw new CompileError("bad type for array size");
+       }
+
+       String desc;
+       exprType = type;
+       arrayDim = dim;
+       if (type == CLASS) {
+           className = resolveClassName(classname);
+           desc = toJvmArrayName(className, dim);
+       }
+       else
+           desc = toJvmTypeName(type, dim);
+
+       bytecode.addMultiNewarray(desc, count);
+    }
+
+    protected void atMethodCall(Expr expr) throws CompileError {
+       String mname = null;
+       CtClass targetClass = null;
+       ASTree method = expr.oprand1();
+       ASTList args = (ASTList)expr.oprand2();
+       boolean isStatic = false;
+       boolean isSpecial = false;
+
+       if (method instanceof Member) {
+           mname = ((Member)method).get();
+           targetClass = thisClass;
+           bytecode.addAload(0);       // this
+       }
+       else if (method instanceof Keyword) {   // constructor
+           isSpecial = true;
+           mname = MethodInfo.nameInit;        // <init>
+           targetClass = thisClass;
+           bytecode.addAload(0);               // this
+           if (((Keyword)method).get() == SUPER)
+               targetClass = getSuperclass(targetClass);
+       }
+       else if (method instanceof Expr) {
+           Expr e = (Expr)method;
+           mname = ((Symbol)e.oprand2()).get();
+           int op = e.getOperator();
+           if (op == MEMBER) {                 // static method
+               targetClass = lookupClass((ASTList)e.oprand1());
+               isStatic = true;
+           }
+           else if (op == '.') {
+               ASTree target = e.oprand1();
+               if (target instanceof Keyword)
+                   if (((Keyword)target).get() == SUPER)
+                       isSpecial = true;
+
+               try {
+                   target.accept(this);
+               }
+               catch (NoFieldException nfe) {
+                   if (nfe.getExpr() != target)
+                       throw nfe;
+
+                   // it should be a static method.
+                   exprType = CLASS;
+                   arrayDim = 0;
+                   className = nfe.getField(); // JVM-internal
+                   isStatic = true;
+               }
+
+               if (arrayDim > 0)
+                   targetClass = lookupClass2(javaLangObject);
+               else if (exprType == CLASS /* && arrayDim == 0 */)
+                   targetClass = lookupClass(className);
+               else
+                   badMethod();
+           }
+           else
+               badMethod();
+       }
+       else
+           fatal();
+
+       atMethodCall2(targetClass, mname, args, isStatic, isSpecial);
+    }
+
+    private static void badMethod() throws CompileError {
+       throw new CompileError("bad method");
+    }
+
+    private static CtClass getSuperclass(CtClass c) throws CompileError {
+       try {
+           return c.getSuperclass();
+       }
+       catch (NotFoundException e) {
+           throw new CompileError("cannot find the super class of "
+                                  + c.getName());
+       }
+    }
+
+    public void atMethodCall2(CtClass targetClass, String mname,
+                       ASTList args, boolean isStatic, boolean isSpecial)
+       throws CompileError
+    {
+       int nargs = atMethodArgsLength(args);
+       int[] types = new int[nargs];
+       int[] dims = new int[nargs];
+       String[] cnames = new String[nargs];
+
+       int stack = bytecode.getStackDepth();
+
+       atMethodArgs(args, types, dims, cnames);
+
+       // used by invokeinterface
+       int count = bytecode.getStackDepth() - stack + 1;
+
+       Object[] found = lookupMethod(targetClass, thisMethod, mname,
+                                     types, dims, cnames, false);
+       if (found == null) {
+           String msg;
+           if (mname.equals(MethodInfo.nameInit))
+               msg = "constructor not found";
+           else
+               msg = "Method " + mname + " not found in "
+                   + targetClass.getName();
+
+           throw new CompileError(msg);
+       }
+
+       CtClass declClass = (CtClass)found[0];
+       MethodInfo minfo = (MethodInfo)found[1];
+       String desc = minfo.getDescriptor();
+       int acc = minfo.getAccessFlags();
+
+       if (mname.equals(MethodInfo.nameInit)) {
+           isSpecial = true;
+           if (declClass != targetClass)
+               throw new CompileError("no such a constructor");
+       }
+       else if ((acc & AccessFlag.PRIVATE) != 0) {
+           isSpecial = true;
+           if (declClass != targetClass)
+               throw new CompileError("Method " + mname + "is private");
+       }
+
+       boolean popTarget = false;
+       if ((acc & AccessFlag.STATIC) != 0) {
+           if (!isStatic) {
+               /* this method is static but the target object is
+                  on stack.  It must be popped out.
+               */
+               isStatic = true;
+               popTarget = true;
+           }
+
+           bytecode.addInvokestatic(declClass, mname, desc);
+       }
+       else if (isSpecial)
+           bytecode.addInvokespecial(declClass, mname, desc);
+       else if (declClass.isInterface())
+           bytecode.addInvokeinterface(declClass, mname, desc, count);
+       else
+           bytecode.addInvokevirtual(declClass, mname, desc);
+
+       setReturnType(desc, isStatic, popTarget);
+    }
+
+    public int atMethodArgsLength(ASTList args) {
+       return ASTList.length(args);
+    }
+
+    public void atMethodArgs(ASTList args, int[] types, int[] dims,
+                            String[] cnames) throws CompileError {
+       int i = 0;
+       while (args != null) {
+           ASTree a = args.head();
+           a.accept(this);
+           types[i] = exprType;
+           dims[i] = arrayDim;
+           cnames[i] = className;
+           ++i;
+           args = args.tail();
+       }
+    }
+
+    private void setReturnType(String desc, boolean isStatic,
+                              boolean popTarget)
+       throws CompileError
+    {
+       int i = desc.indexOf(')');
+       if (i < 0)
+           badMethod();
+
+       char c = desc.charAt(++i);
+       int dim = 0;
+       while (c == '[') {
+           ++dim;
+           c = desc.charAt(++i);
+       }
+
+       arrayDim = dim;
+       if (c == 'L') {
+           int j = desc.indexOf(';', i + 1);
+           if (j < 0)
+               badMethod();
+
+           exprType = CLASS;
+           className = desc.substring(i + 1, j);
+       }
+       else {
+           exprType = descToType(c);
+           className = null;
+       }
+
+       int etype = exprType;
+       if (isStatic) {
+           if (popTarget) {
+               if (is2word(etype, dim)) {
+                   bytecode.addOpcode(DUP2_X1);
+                   bytecode.addOpcode(POP2);
+                   bytecode.addOpcode(POP);
+               }
+               else if (etype == VOID)
+                   bytecode.addOpcode(POP);
+               else {
+                   bytecode.addOpcode(SWAP);
+                   bytecode.addOpcode(POP);
+               }
+           }
+       }
+    }
+
+    private Object[] lookupMethod(CtClass clazz, MethodInfo current,
+                                 String methodName,
+                                 int[] argTypes, int[] argDims,
+                                 String[] argClassNames, boolean onlyExact)
+       throws CompileError
+    {
+       Object[] maybe = null;
+
+       if (current != null)
+           if (current.getName().equals(methodName)) {
+               int res = compareSignature(current.getDescriptor(),
+                                          argTypes, argDims, argClassNames);
+               Object[] r = new Object[] { clazz, current };
+               if (res == YES)
+                   return r;
+               else if (res == MAYBE && maybe == null)
+                   maybe = r;
+           }
+
+       List list = clazz.getClassFile2().getMethods();
+       int n = list.size();
+       for (int i = 0; i < n; ++i) {
+           MethodInfo minfo = (MethodInfo)list.get(i);
+           if (minfo.getName().equals(methodName)) {
+               int res = compareSignature(minfo.getDescriptor(),
+                                          argTypes, argDims, argClassNames);
+               Object[] r = new Object[] { clazz, minfo };
+               if (res == YES)
+                   return r;
+               else if (res == MAYBE && maybe == null)
+                   maybe = r;
+           }
+       }
+
+       try {
+           CtClass pclazz = clazz.getSuperclass();
+           if (pclazz != null) {
+               Object[] r = lookupMethod(pclazz, null, methodName, argTypes,
+                                         argDims, argClassNames,
+                                         (onlyExact || maybe != null));
+               if (r != null)
+                   return r;
+           }
+       }
+       catch (NotFoundException e) {}
+
+       /* -- not necessary to search implemented interfaces.
+       try {
+           CtClass[] ifs = clazz.getInterfaces();
+           int size = ifs.length;
+           for (int i = 0; i < size; ++i) {
+               Object[] r = lookupMethod(ifs[i], methodName, argTypes,
+                                         argDims, argClassNames);
+               if (r != null)
+                   return r;
+           }
+       }
+       catch (NotFoundException e) {}
+       */
+
+       if (onlyExact)
+           return null;
+       else
+           return maybe;
+    }
+
+    private static final int YES = 2;
+    private static final int MAYBE = 1;
+    private static final int NO = 0;
+
+    /*
+     * Returns YES if actual parameter types matches the given signature.
+     *
+     * argTypes, argDims, and argClassNames represent actual parameters.
+     *
+     * This method does not correctly implement the Java method dispatch
+     * algorithm.
+     */
+    private int compareSignature(String desc, int[] argTypes,
+                                int[] argDims, String[] argClassNames)
+       throws CompileError
+    {
+       int result = YES;
+       int i = 1;
+       int nArgs = argTypes.length;
+       if (nArgs != Descriptor.numOfParameters(desc))
+           return NO;
+
+       int len = desc.length();
+       for (int n = 0; i < len; ++n) {
+           char c = desc.charAt(i++);
+           if (c == ')')
+               return (n == nArgs ? result : NO);
+           else if (n >= nArgs)
+               return NO;
+
+           int dim = 0;
+           while (c == '[') {
+               ++dim;
+               c = desc.charAt(i++);
+           }
+
+           if (argTypes[n] == NULL) {
+               if (dim == 0 && c != 'L')
+                   return NO;
+           }
+           else if (argDims[n] != dim) {
+               if (!(dim == 0 && c == 'L'
+                     && desc.startsWith("java/lang/Object;", i)))
+                   return NO;
+
+               // if the thread reaches here, c must be 'L'.
+               i = desc.indexOf(';', i) + 1;
+               result = MAYBE;
+               if (i <= 0)
+                   return NO;  // invalid descriptor?
+           }
+           else if (c == 'L') {        // not compare
+               int j = desc.indexOf(';', i);
+               if (j < 0 || argTypes[n] != CLASS)
+                   return NO;
+
+               String cname = desc.substring(i, j);
+               if (!cname.equals(argClassNames[n])) {
+                   CtClass clazz = lookupClass(argClassNames[n]);
+                   try {
+                       if (clazz.subtypeOf(lookupClass(cname)))
+                           result = MAYBE;
+                       else
+                           return NO;
+                   }
+                   catch (NotFoundException e) {
+                       result = MAYBE; // should be NO?
+                   }
+               }
+
+               i = j + 1;
+           }
+           else {
+               int t = descToType(c);
+               int at = argTypes[n];
+               if (t != at)
+                   if (t == INT
+                       && (at == SHORT || at == BYTE || at == CHAR))
+                       result = MAYBE;
+                   else
+                       return NO;
+           }
+       }
+
+       return NO;
+    }
+
+    protected static int descToType(char c) throws CompileError {
+       switch (c) {
+       case 'Z' :
+           return BOOLEAN;
+       case 'C' :
+           return CHAR;
+       case 'B' :
+           return  BYTE;
+       case 'S' :
+           return SHORT;
+       case 'I' :
+           return INT;
+       case 'J' :
+           return LONG;
+       case 'F' :
+           return FLOAT;
+       case 'D' :
+           return DOUBLE;
+       case 'V' :
+           return VOID;
+       case 'L' :
+       case '[' :
+           return CLASS;
+       default :
+           fatal();
+           return VOID;
+       }
+    }
+
+    protected void atFieldAssign(Expr expr, int op, ASTree left,
+                       ASTree right, boolean doDup) throws CompileError
+    {
+       CtField f = fieldAccess(left);
+       boolean is_static = resultStatic;
+       if (op != '=' && !is_static)
+           bytecode.addOpcode(DUP);
+
+       int fi = atFieldRead(f, is_static, op == '=');
+       int fType = exprType;
+       int fDim = arrayDim;
+       String cname = className;
+
+       atAssignCore(expr, op, right, fType, fDim, cname);
+
+       boolean is2w = is2word(fType, fDim);
+       if (doDup) {
+           int dup_code;
+           if (is_static)
+               dup_code = (is2w ? DUP2 : DUP);
+           else
+               dup_code = (is2w ? DUP2_X1 : DUP_X1);
+
+           bytecode.addOpcode(dup_code);
+       }
+
+       if (is_static) {
+           bytecode.add(PUTSTATIC);
+           bytecode.growStack(is2w ? -2 : -1);
+       }
+       else {
+           bytecode.add(PUTFIELD);
+           bytecode.growStack(is2w ? -3 : -2);
+       }
+
+       bytecode.addIndex(fi);
+       exprType = fType;
+       arrayDim = fDim;
+       className = cname;
+    }
+
+    /* overwritten in JvstCodeGen.
+     */
+    public void atMember(Member mem) throws CompileError {
+       atFieldRead(mem);
+    }
+
+    protected void atFieldRead(ASTree expr) throws CompileError
+    {
+       CtField f = fieldAccess(expr);
+       boolean is_static = resultStatic;
+       atFieldRead(f, is_static, false);
+    }
+
+    private int atFieldRead(CtField f, boolean isStatic, boolean noRead)
+       throws CompileError
+    {
+       FieldInfo finfo = f.getFieldInfo2();
+       String type = finfo.getDescriptor();
+
+       int fi = addFieldrefInfo(f, finfo, type);
+
+       int i = 0;
+       char c = type.charAt(i);
+       boolean is2byte = (c == 'J' || c == 'D');
+       exprType = descToType(c);
+       arrayDim = 0;
+       if (c == '[') {
+           i = 1;
+           while ((c = type.charAt(i)) == '[')
+               ++i;
+
+           arrayDim = i;
+       }
+
+       if (c == 'L')
+           className = type.substring(i + 1, type.indexOf(';', i + 1));
+
+       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;
+    }
+
+    protected int addFieldrefInfo(CtField f, FieldInfo finfo, String type) {
+       ConstPool cp = bytecode.getConstPool();
+       String cname = f.getDeclaringClass().getName();
+       int ci = cp.addClassInfo(cname);
+       String name = finfo.getName();
+       return cp.addFieldrefInfo(ci, name, type);
+    }
+
+    protected void atFieldPlusPlus(int token, boolean isPost,
+                                  ASTree oprand, Expr expr, boolean doDup)
+       throws CompileError
+    {
+       CtField f = fieldAccess(oprand);
+       boolean is_static = resultStatic;
+       if (!is_static)
+           bytecode.addOpcode(DUP);
+
+       int fi = atFieldRead(f, is_static, false);
+       int t = exprType;
+       boolean is2w = is2word(t, arrayDim);
+
+       int dup_code;
+       if (is_static)
+           dup_code = (is2w ? DUP2 : DUP);
+       else
+           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);
+    }
+
+    /* This method also returns a value in resultStatic.
+     */
+    protected CtField fieldAccess(ASTree expr) throws CompileError {
+       CtField f = null;
+       boolean is_static = false;
+       if (expr instanceof Member) {
+           String name = ((Member)expr).get();
+           try {
+               f = thisClass.getField(name);
+           }
+           catch (NotFoundException e) {
+               // EXPR might be part of a static member access?
+               throw new NoFieldException(name, expr);
+           }
+
+           is_static = Modifier.isStatic(f.getModifiers());
+           if (!is_static)
+               if (inStaticMethod)
+                   throw new CompileError(
+                               "not available in a static method: " + name);
+               else
+                   bytecode.addAload(0);       // this
+       }
+       else if (expr instanceof Expr) {
+           Expr e = (Expr)expr;
+           int op = e.getOperator();
+           if (op == MEMBER) {
+               f = lookupField((ASTList)e.oprand1(), (Symbol)e.oprand2());
+               is_static = true;
+           }
+           else if (op == '.') {
+               try {
+                   e.oprand1().accept(this);
+                   if (exprType == CLASS && arrayDim == 0)
+                       f = lookupField(className, (Symbol)e.oprand2());
+                   else
+                       badLvalue();
+
+                   is_static = Modifier.isStatic(f.getModifiers());
+                   if (is_static)
+                       bytecode.addOpcode(POP);
+               }
+               catch (NoFieldException nfe) {
+                   if (nfe.getExpr() != e.oprand1())
+                       throw nfe;
+
+                   Symbol fname = (Symbol)e.oprand2();
+                   // it should be a static field.
+                   try {
+                       f = lookupField(nfe.getField(), fname);
+                       is_static = true;
+                   }
+                   catch (CompileError ce) {
+                       // EXPR might be part of a qualified class name.
+                       throw new NoFieldException(nfe.getField() + "/"
+                                                  + fname.get(), expr);
+                   }
+               }
+           }
+           else
+               badLvalue();
+       }
+       else
+           badLvalue();
+
+       resultStatic = is_static;
+       return f;
+    }
+
+    private static void badLvalue() throws CompileError {
+       throw new CompileError("bad l-value");
+    }
+
+    public CtClass[] makeParamList(MethodDecl md) throws CompileError {
+       CtClass[] params;
+       ASTList plist = md.getParams();
+       if (plist == null)
+           params = new CtClass[0];
+       else {
+           int i = 0;
+           params = new CtClass[plist.length()];
+           while (plist != null) {
+               params[i++] = lookupClass((Declarator)plist.head());
+               plist = plist.tail();
+           }
+       }
+
+       return params;
+    }
+
+    public CtClass[] makeThrowsList(MethodDecl md) throws CompileError {
+       CtClass[] clist;
+       ASTList list = md.getThrows();
+       if (list == null)
+           return null;
+       else {
+           int i = 0;
+           clist = new CtClass[list.length()];
+           while (list != null) {
+               clist[i++] = lookupClass((ASTList)list.head());
+               list = list.tail();
+           }
+
+           return clist;
+       }
+    }
+
+    public static int getModifiers(ASTList mods) {
+       int m = 0;
+       while (mods != null) {
+           Keyword k = (Keyword)mods.head();
+           mods = mods.tail();
+           switch (k.get()) {
+           case STATIC :
+               m |= Modifier.STATIC;
+               break;
+           case FINAL :
+               m |= Modifier.FINAL;
+               break;
+           case SYNCHRONIZED :
+               m |= Modifier.SYNCHRONIZED;
+               break;
+           case ABSTRACT :
+               m |= Modifier.ABSTRACT;
+               break;
+           case PUBLIC :
+               m |= Modifier.PUBLIC;
+               break;
+           case PROTECTED :
+               m |= Modifier.PROTECTED;
+               break;
+           case PRIVATE :
+               m |= Modifier.PRIVATE;
+               break;
+           case VOLATILE :
+               m |= Modifier.VOLATILE;
+               break;
+           case TRANSIENT :
+               m |= Modifier.TRANSIENT;
+               break;
+           case STRICT :
+               m |= Modifier.STRICT;
+               break;
+           }
+       }
+
+       return m;
+    }
+
+    /* Converts a class name into a JVM-internal representation.
+     *
+     * It may also expand a simple class name to java.lang.*.
+     * For example, this converts Object into java/lang/Object.
+     */
+    protected String resolveClassName(ASTList name) throws CompileError {
+       if (name == null)
+           return null;
+       else
+           return javaToJvmName(lookupClass(name).getName());
+    }
+
+    /* Expands a simple class name to java.lang.*.
+     * For example, this converts Object into java/lang/Object.
+     */
+    protected String resolveClassName(String jvmName) throws CompileError {
+       if (jvmName == null)
+           return null;
+       else
+           return javaToJvmName(lookupClass(jvmName).getName());
+    }
+
+    protected CtClass lookupClass(Declarator decl) throws CompileError {
+       return lookupClass(decl.getType(), decl.getArrayDim(),
+                          decl.getClassName());
+    }
+
+    protected CtClass lookupClass(int type, int dim, String classname)
+       throws CompileError
+    {
+       String cname = "";
+       CtClass clazz;
+       switch (type) {
+       case CLASS :
+           clazz = lookupClass(classname);
+           if (dim > 0)
+               cname = clazz.getName();
+           else
+               return clazz;
+
+           break;
+       case BOOLEAN :
+           cname = "boolean";
+           break;
+       case CHAR :
+           cname = "char";
+           break;
+       case BYTE :
+           cname = "byte";
+           break;
+       case SHORT :
+           cname = "short";
+           break;
+       case INT :
+           cname = "int";
+           break;
+       case LONG :
+           cname = "long";
+           break;
+       case FLOAT :
+           cname = "float";
+           break;
+       case DOUBLE :
+           cname = "double";
+           break;
+       case VOID :
+           cname = "void";
+           break;
+       default :
+           fatal();
+       }
+
+       while (dim-- > 0)
+           cname += "[]";
+
+       return lookupClass2(cname);
+    }
+
+    protected CtClass lookupClass(ASTList name) throws CompileError {
+       return lookupClass2(Declarator.astToClassName(name, '.'));
+    }
+
+    protected CtClass lookupClass(String jvmName) throws CompileError {
+       return lookupClass2(jvmToJavaName(jvmName));
+    }
+
+    /**
+     * @param name     a qualified class name. e.g. java.lang.String
+     */
+    private CtClass lookupClass2(String name) throws CompileError {
+       try {
+           return classPool.get(name);
+       }
+       catch (NotFoundException e) {}
+
+       try {
+           if (name.indexOf('.') < 0)
+               return classPool.get("java.lang." + name);
+       }
+       catch (NotFoundException e) {}
+
+       throw new CompileError("no such class: " + name);
+    }
+
+    public CtField lookupField(ASTList className, Symbol fieldName)
+       throws CompileError
+    {
+       return lookupField2(Declarator.astToClassName(className, '.'),
+                           fieldName);
+    }
+
+    public CtField lookupField(String className, Symbol fieldName)
+       throws CompileError
+    {
+       return lookupField2(jvmToJavaName(className), fieldName);
+    }
+
+    /**
+     * @param name     a qualified class name. e.g. java.lang.String
+     */
+    private CtField lookupField2(String className, Symbol fieldName)
+       throws CompileError
+    {
+       CtClass cc = lookupClass(className);
+       try {
+           return cc.getField(fieldName.get());
+       }
+       catch (NotFoundException e) {}
+       throw new CompileError("no such field: " + fieldName.get());
+    }
+
+    protected static String javaToJvmName(String classname) {
+       return classname.replace('.', '/');
+    }
+
+    protected static String jvmToJavaName(String classname) {
+       return classname.replace('/', '.');
+    }
+}
diff --git a/src/main/javassist/compiler/NoFieldException.java b/src/main/javassist/compiler/NoFieldException.java
new file mode 100644 (file)
index 0000000..a810286
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+import javassist.compiler.ast.ASTree;
+
+public class NoFieldException extends CompileError {
+    private String fieldName;
+    private ASTree expr;
+
+    /* NAME must be JVM-internal representation.
+     */
+    public NoFieldException(String name, ASTree e) {
+       super("no such field: " + name);
+       fieldName = name;
+       expr = e;
+    }
+
+    /* The returned name should be JVM-internal representation.
+     */
+    public String getField() { return fieldName; }
+
+    /* Returns the expression where this exception is thrown.
+     */
+    public ASTree getExpr() { return expr; }
+}
diff --git a/src/main/javassist/compiler/Parser.java b/src/main/javassist/compiler/Parser.java
new file mode 100644 (file)
index 0000000..3039e5d
--- /dev/null
@@ -0,0 +1,1138 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+import javassist.compiler.ast.*;
+
+public final class Parser implements TokenId {
+    private Lex lex;
+
+    public Parser(Lex lex) {
+       this.lex = lex;
+    }
+
+    public boolean hasMore() { return lex.lookAhead() >= 0; }
+
+    /* member.declaration
+     * : method.declaration | field.declaration
+     */
+    public ASTList parseMember(SymbolTable tbl) throws CompileError {
+       ASTList mem = parseMember1(tbl);
+       if (mem instanceof MethodDecl)
+           return parseMethod2(tbl, (MethodDecl)mem);
+       else
+           return mem;
+    }
+
+    /* A method body is not parsed.
+     */
+    public ASTList parseMember1(SymbolTable tbl) throws CompileError {
+       ASTList mods = parseMemberMods();
+       Declarator d;
+       boolean isConstructor = false;
+       if (lex.lookAhead() == Identifier && lex.lookAhead(1) == '(') {
+           d = new Declarator(VOID, 0);
+           isConstructor = true;
+       }
+       else
+           d = parseFormalType(tbl);
+
+       if (lex.get() != Identifier)
+           throw new SyntaxError(lex);
+
+       String name;
+       if (isConstructor)
+           name = MethodDecl.initName;
+       else
+           name = lex.getString();
+
+       d.setVariable(new Symbol(name));
+       if (isConstructor || lex.lookAhead() == '(')
+           return parseMethod1(tbl, isConstructor, mods, d);
+       else
+           return parseField(tbl, mods, d);
+    }
+
+    /* field.declaration
+     *  : member.modifiers
+     *    formal.type Identifier
+     *    [ "=" expression ] ";"
+     */
+    private FieldDecl parseField(SymbolTable tbl, ASTList mods,
+                               Declarator d) throws CompileError
+    {
+       ASTree expr = null;
+       if (lex.lookAhead() == '=') {
+           lex.get();
+           expr = parseExpression(tbl);
+       }
+
+       int c = lex.get();
+       if (c == ';')
+           return new FieldDecl(mods, new ASTList(d, new ASTList(expr)));
+       else if (c == ',')
+           throw new CompileError(
+               "only one field can be declared in one declaration", lex);
+       else
+           throw new SyntaxError(lex);
+    }
+
+    /* method.declaration
+     *  : member.modifiers
+     *    [ formal.type ]
+     *    Identifier "(" [ formal.parameter ( "," formal.parameter )* ] ")"
+     *    array.dimension
+     *    [ THROWS class.type ( "," class.type ) ]
+     *    ( block.statement | ";" )
+     *
+     * Note that a method body is not parsed.
+     */
+    private MethodDecl parseMethod1(SymbolTable tbl, boolean isConstructor,
+                                   ASTList mods, Declarator d)
+       throws CompileError
+    {
+       if (lex.get() != '(')
+           throw new SyntaxError(lex);
+
+       ASTList parms = null;
+       if (lex.lookAhead() != ')')
+           while (true) {
+               parms = ASTList.append(parms, parseFormalParam(tbl));
+               int t = lex.lookAhead();
+               if (t == ',')
+                   lex.get();
+               else if (t == ')')
+                   break;
+           }
+
+       lex.get();      // ')'
+       d.addArrayDim(parseArrayDimension());
+       if (isConstructor && d.getArrayDim() > 0)
+           throw new SyntaxError(lex);
+
+       ASTList throwsList = null;
+       if (lex.lookAhead() == THROWS) {
+           lex.get();
+           while (true) {
+               throwsList = ASTList.append(throwsList, parseClassType(tbl));
+               if (lex.lookAhead() == ',')
+                   lex.get();
+               else
+                   break;
+           }
+       }
+
+       return new MethodDecl(mods, new ASTList(d,
+                               ASTList.make(parms, throwsList, null)));
+    }
+
+    /* Parses a method body.
+     */
+    public MethodDecl parseMethod2(SymbolTable tbl, MethodDecl md)
+       throws CompileError
+    {
+       Stmnt body = null;
+       if (lex.lookAhead() == ';')
+           lex.get();
+       else {
+           body = parseBlock(tbl);
+           if (body == null)
+               body = new Stmnt(BLOCK);
+       }
+
+       md.sublist(4).setHead(body);
+       return md;
+    }
+
+    /* member.modifiers
+     *  : ( FINAL | SYNCHRONIZED | ABSTRACT
+     *    | PUBLIC | PROTECTED | PRIVATE | STATIC
+     *    | VOLATILE | TRANSIENT | STRICT )*
+     */
+    private ASTList parseMemberMods() {
+       int t;
+       ASTList list = null;
+       while (true) {
+           t = lex.lookAhead();
+           if (t == ABSTRACT || t == FINAL || t == PUBLIC || t == PROTECTED
+               || t == PRIVATE || t == SYNCHRONIZED || t == STATIC
+               || t == VOLATILE || t == TRANSIENT || t == STRICT)
+               list = new ASTList(new Keyword(lex.get()), list);
+           else
+               break;
+       }
+
+       return list;
+    }
+
+    /* formal.type : ( build-in-type | class.type ) array.dimension
+     */
+    private Declarator parseFormalType(SymbolTable tbl) throws CompileError {
+       int t = lex.lookAhead();
+       if (isBuiltinType(t) || t == VOID) {
+           lex.get();  // primitive type
+           int dim = parseArrayDimension();
+           return new Declarator(t, dim);
+       }
+       else {
+           ASTList name = parseClassType(tbl);
+           int dim = parseArrayDimension();
+           return new Declarator(name, dim);
+       }
+    }
+
+    private static boolean isBuiltinType(int t) {
+       return (t == BOOLEAN || t == BYTE || t == CHAR || t == SHORT
+               || t == INT || t == LONG || t == FLOAT || t == DOUBLE);
+    }
+
+    /* formal.parameter : formal.type Identifier array.dimension
+     */
+    private Declarator parseFormalParam(SymbolTable tbl)
+       throws CompileError
+    {
+       Declarator d = parseFormalType(tbl);
+       if (lex.get() != Identifier)
+           throw new SyntaxError(lex);
+
+       String name = lex.getString();
+       d.setVariable(new Symbol(name));
+       d.addArrayDim(parseArrayDimension());
+       tbl.append(name, d);
+       return d;
+    }
+
+    /* statement : [ label ":" ]* labeled.statement
+     *
+     * labeled.statement
+     *         : block.statement
+     *         | if.statement
+     *         | while.statement
+     *         | do.statement
+     *         | for.statement
+     *         | switch.statement
+     *         | try.statement
+     *         | return.statement
+     *         | thorw.statement
+     *         | break.statement
+     *         | continue.statement
+     *         | declaration.or.expression
+     *         | ";"
+     *
+     * This method may return null (empty statement).
+     */
+    public Stmnt parseStatement(SymbolTable tbl)
+       throws CompileError
+    {
+       int t = lex.lookAhead();
+       if (t == '{')
+           return parseBlock(tbl);
+       else if (t == ';') {
+           lex.get();
+           return new Stmnt(BLOCK);    // empty statement
+       }
+       else if (t == Identifier && lex.lookAhead(1) == ':') {
+           lex.get();  // Identifier
+           String label = lex.getString();
+           lex.get();  // ':'
+           return Stmnt.make(LABEL, new Symbol(label), parseStatement(tbl));
+       }
+       else if (t == IF)
+           return parseIf(tbl);
+       else if (t == WHILE)
+           return parseWhile(tbl);
+       else if (t == DO)
+           return parseDo(tbl);
+       else if (t == FOR)
+           return parseFor(tbl);
+       else if (t == TRY)
+           return parseTry(tbl);
+       else if (t == SWITCH)
+           return parseSwitch(tbl);
+       else if (t == RETURN)
+           return parseReturn(tbl);
+       else if (t == THROW)
+           return parseThrow(tbl);
+       else if (t == BREAK)
+           return parseBreak(tbl);
+       else if (t == CONTINUE)
+           return parseContinue(tbl);
+       else
+           return parseDeclarationOrExpression(tbl, false);
+    }
+
+    /* block.statement : "{" statement* "}"
+     */
+    private Stmnt parseBlock(SymbolTable tbl) throws CompileError {
+       if (lex.get() != '{')
+           throw new SyntaxError(lex);
+
+       Stmnt body = null;
+       SymbolTable tbl2 = new SymbolTable(tbl);
+       while (lex.lookAhead() != '}') {
+           Stmnt s = parseStatement(tbl2);
+           if (s != null)
+               body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s));
+       }
+
+       lex.get();      // '}'
+       if (body == null)
+           return new Stmnt(BLOCK);    // empty block
+       else
+           return body;
+    }
+
+    /* if.statement : IF "(" expression ")" statement
+     *               [ ELSE statement ]
+     */
+    private Stmnt parseIf(SymbolTable tbl) throws CompileError {
+       int t = lex.get();      // IF
+       if (lex.get() != '(')
+           throw new SyntaxError(lex);
+
+       ASTree expr = parseExpression(tbl);
+       if (lex.get() != ')')
+           throw new SyntaxError(lex);
+
+       Stmnt thenp = parseStatement(tbl);
+       Stmnt elsep;
+       if (lex.lookAhead() == ELSE) {
+           lex.get();
+           elsep = parseStatement(tbl);
+       }
+       else
+           elsep = null;
+
+       return new Stmnt(t, expr, new ASTList(thenp, new ASTList(elsep)));
+    }
+
+    /* while.statement : WHILE "(" expression ")" statement
+     */
+    private Stmnt parseWhile(SymbolTable tbl)
+       throws CompileError
+    {
+       int t = lex.get();      // WHILE
+       if (lex.get() != '(')
+           throw new SyntaxError(lex);
+
+       ASTree expr = parseExpression(tbl);
+       if (lex.get() != ')')
+           throw new SyntaxError(lex);
+
+       Stmnt body = parseStatement(tbl);
+       return new Stmnt(t, expr, body);
+    }
+
+    /* do.statement : DO statement WHILE "(" expression ")" ";"
+     */
+    private Stmnt parseDo(SymbolTable tbl) throws CompileError {
+       int t = lex.get();      // DO
+       Stmnt body = parseStatement(tbl);
+       if (lex.get() != WHILE || lex.get() != '(')
+           throw new SyntaxError(lex);
+
+       ASTree expr = parseExpression(tbl);
+       if (lex.get() != ')' || lex.get() != ';')
+           throw new SyntaxError(lex);
+
+       return new Stmnt(t, expr, body);
+    }
+
+    /* for.statement : FOR "(" decl.or.expr expression ";" expression ")"
+     *                statement
+     */
+    private Stmnt parseFor(SymbolTable tbl) throws CompileError {
+       Stmnt expr1, expr3;
+       ASTree expr2;
+       int t = lex.get();      // FOR
+
+       SymbolTable tbl2 = new SymbolTable(tbl);
+
+       if (lex.get() != '(')
+           throw new SyntaxError(lex);
+
+       if (lex.lookAhead() == ';') {
+           lex.get();
+           expr1 = null;
+       }
+       else
+           expr1 = parseDeclarationOrExpression(tbl2, true);
+
+       if (lex.lookAhead() == ';')
+           expr2 = null;
+       else
+           expr2 = parseExpression(tbl2);
+
+       if (lex.get() != ';')
+           throw new CompileError("; is missing", lex);
+
+       if (lex.lookAhead() == ')')
+           expr3 = null;
+       else
+           expr3 = parseExprList(tbl2);
+
+       if (lex.get() != ')')
+           throw new CompileError(") is missing", lex);
+
+       Stmnt body = parseStatement(tbl2);
+       return new Stmnt(t, expr1, new ASTList(expr2,
+                                              new ASTList(expr3, body)));
+    }
+
+    /* switch.statement : SWITCH "(" expression ")" "{" switch.block "}"
+     *
+     * swtich.block : ( switch.label* statement )*
+     *
+     * swtich.label : DEFAULT ":"
+     *             | CASE const.expression ":"
+     */
+    private Stmnt parseSwitch(SymbolTable tbl) throws CompileError {
+       throw new CompileError("switch is not supported", lex);
+    }
+
+    /* try.statement
+     * : TRY block.statement
+     *  [ CATCH "(" class.type Identifier ")" block.statement ]*
+     *  [ FINALLY block.statement ]*
+     */
+    private Stmnt parseTry(SymbolTable tbl) throws CompileError {
+       lex.get();      // TRY
+       Stmnt block = parseBlock(tbl);
+       ASTList catchList = null;
+       while (lex.lookAhead() == CATCH) {
+           lex.get();  // CATCH
+           if (lex.get() != '(')
+               throw new SyntaxError(lex);
+
+           SymbolTable tbl2 = new SymbolTable(tbl);
+           Declarator d = parseFormalParam(tbl2);
+           if (d.getArrayDim() > 0 || d.getType() != CLASS)
+               throw new SyntaxError(lex);
+
+           if (lex.get() != ')')
+               throw new SyntaxError(lex);
+
+           Stmnt b = parseBlock(tbl2);
+           catchList = ASTList.append(catchList, new Pair(d, b));
+       }
+
+       Stmnt finallyBlock = null;
+       if (lex.lookAhead() == FINALLY) {
+           lex.get();  // FINALLY
+           finallyBlock = parseBlock(tbl);
+       }
+
+       return Stmnt.make(TRY, block, catchList, finallyBlock);
+    }
+
+    /* return.statement : RETURN [ expression ] ";"
+     */
+    private Stmnt parseReturn(SymbolTable tbl) throws CompileError {
+       int t = lex.get();      // RETURN
+       Stmnt s = new Stmnt(t);
+       if (lex.lookAhead() != ';')
+           s.setLeft(parseExpression(tbl));
+
+       if (lex.get() != ';')
+           throw new CompileError("; is missing", lex);
+
+       return s;
+    }
+
+    /* throw.statement : THROW expression ";"
+     */
+    private Stmnt parseThrow(SymbolTable tbl) throws CompileError {
+       int t = lex.get();      // THROW
+       ASTree expr = parseExpression(tbl);
+       if (lex.get() != ';')
+           throw new CompileError("; is missing", lex);
+
+       return new Stmnt(t, expr);
+    }
+
+    /* break.statement : BREAK [ Identifier ] ";"
+     */
+    private Stmnt parseBreak(SymbolTable tbl)
+       throws CompileError
+    {
+       return parseContinue(tbl);
+    }
+
+    /* continue.statement : CONTINUE [ Identifier ] ";"
+     */
+    private Stmnt parseContinue(SymbolTable tbl)
+       throws CompileError
+    {
+       int t = lex.get();      // CONTINUE
+       Stmnt s = new Stmnt(t);
+       int t2 = lex.get();
+       if (t2 == Identifier) {
+           s.setLeft(new Symbol(lex.getString()));
+           t2 = lex.get();
+       }
+
+       if (t2 != ';')
+           throw new CompileError("; is missing", lex);
+
+       return s;
+    }
+
+    /* declaration.or.expression
+     *      : [ FINAL ] built-in-type array.dimension declarators
+     *      | [ FINAL ] class.type array.dimension declarators
+     *      | expression ';'
+     *     | expr.list ';'             if exprList is true
+     *
+     * Note: FINAL is currently ignored.  This must be fixed
+     * in future.
+     */
+    private Stmnt parseDeclarationOrExpression(SymbolTable tbl,
+                                              boolean exprList)
+       throws CompileError
+    {
+       int t = lex.lookAhead();
+       while (t == FINAL) {
+           lex.get();
+           t = lex.lookAhead();
+       }
+
+       if (isBuiltinType(t)) {
+           t = lex.get();
+           int dim = parseArrayDimension();
+           return parseDeclarators(tbl, new Declarator(t, dim));
+       }
+       else if (t == Identifier) {
+           int i = nextIsClassType(0);
+           if (i >= 0)
+               if (lex.lookAhead(i) == Identifier) {
+                   ASTList name = parseClassType(tbl);
+                   int dim = parseArrayDimension();
+                   return parseDeclarators(tbl, new Declarator(name, dim));
+               }
+       }
+
+       Stmnt expr;
+       if (exprList)
+           expr = parseExprList(tbl);
+       else
+           expr = new Stmnt(EXPR, parseExpression(tbl));
+
+       if (lex.get() != ';')
+           throw new CompileError("; is missing", lex);
+
+       return expr;
+    }
+
+    /* expr.list : ( expression ',')* expression
+     */
+    private Stmnt parseExprList(SymbolTable tbl) throws CompileError {
+       Stmnt expr = null;
+       for (;;) {
+           Stmnt e = new Stmnt(EXPR, parseExpression(tbl));
+           expr = (Stmnt)ASTList.concat(expr, new Stmnt(BLOCK, e));
+           if (lex.lookAhead() == ',')
+               lex.get();
+           else
+               return expr;
+       }
+    }
+
+    /* declarators : declarator [ ',' declarator ]* ';'
+     */
+    private Stmnt parseDeclarators(SymbolTable tbl, Declarator d)
+       throws CompileError
+    {
+       Stmnt decl = null;
+       for (;;) {
+           decl = (Stmnt)ASTList.concat(decl,
+                               new Stmnt(DECL, parseDeclarator(tbl, d)));
+           int t = lex.get();
+           if (t == ';')
+               return decl;
+           else if (t != ',')
+               throw new CompileError("; is missing", lex);
+       }
+    }
+
+    /* declarator : Identifier array.dimension [ '=' initializer ]
+     */
+    private Declarator parseDeclarator(SymbolTable tbl, Declarator d)
+       throws CompileError
+    {
+       if (lex.get() != Identifier || d.getType() == VOID)
+           throw new SyntaxError(lex);
+
+       String name = lex.getString();
+       Symbol symbol = new Symbol(name);
+       int dim = parseArrayDimension();
+       ASTree init = null;
+       if (lex.lookAhead() == '=') {
+           lex.get();
+           init = parseInitializer(tbl);
+       }
+
+       Declarator decl = d.make(symbol, dim, init);
+       tbl.append(name, decl);
+       return decl;
+    }
+
+    /* initializer : expression | array.initializer
+     */
+    private ASTree parseInitializer(SymbolTable tbl) throws CompileError {
+       if (lex.lookAhead() == '{')
+           return parseArrayInitializer(tbl);
+       else
+           return parseExpression(tbl);
+    }
+
+    /* array.initializer :
+     *  '{' (( array.initializer | expression ) ',')* '}'
+     */
+    private ASTree parseArrayInitializer(SymbolTable tbl)
+       throws CompileError
+    {
+       lex.get();      // '{'
+       throw new CompileError("array initializer is not supported", lex);
+    }
+
+    /* expression : conditional.expr
+     *           | conditional.expr assign.op expression (right-to-left)
+     */
+    public ASTree parseExpression(SymbolTable tbl) throws CompileError {
+       ASTree left = parseConditionalExpr(tbl);
+       if (!isAssignOp(lex.lookAhead()))
+           return left;
+
+       int t = lex.get();
+       ASTree right = parseExpression(tbl);
+       return AssignExpr.makeAssign(t, left, right);
+    }
+
+    private static boolean isAssignOp(int t) {
+       return t == '=' || t == MOD_E || t == AND_E
+               || t == MUL_E || t == PLUS_E || t == MINUS_E || t == DIV_E
+               || t == EXOR_E || t == OR_E || t == LSHIFT_E
+               || t == RSHIFT_E || t == ARSHIFT_E;
+    }
+
+    /* conditional.expr                        (right-to-left)
+     *    : logical.or.expr [ '?' expression ':' conditional.expr ]
+     */
+    private ASTree parseConditionalExpr(SymbolTable tbl) throws CompileError {
+       ASTree cond = parseBinaryExpr(tbl);
+       if (lex.lookAhead() == '?') {
+           lex.get();
+           ASTree thenExpr = parseExpression(tbl);
+           if (lex.get() != ':')
+               throw new CompileError(": is missing", lex);
+
+           ASTree elseExpr = parseExpression(tbl);
+           return new CondExpr(cond, thenExpr, elseExpr);
+       }
+       else
+           return cond;
+    }
+
+    /* logical.or.expr         10 (operator precedence)
+     * : logical.and.expr
+     * | logical.or.expr OROR logical.and.expr         left-to-right
+     *
+     * logical.and.expr                9
+     * : inclusive.or.expr
+     * | logical.and.expr ANDAND inclusive.or.expr
+     *
+     * inclusive.or.expr       8
+     * : exclusive.or.expr
+     * | inclusive.or.expr "|" exclusive.or.expr
+     *
+     * exclusive.or.expr       7
+     *  : and.expr
+     * | exclusive.or.expr "^" and.expr
+     *
+     * and.expr                        6
+     * : equality.expr
+     * | and.expr "&" equality.expr
+     *
+     * equality.expr           5
+     * : relational.expr
+     * | equality.expr (EQ | NEQ) relational.expr
+     *
+     * relational.expr         4
+     * : shift.expr
+     * | relational.expr (LE | GE | "<" | ">") shift.expr
+     * | relational.expr INSTANCEOF class.type ("[" "]")*
+     *
+     * shift.expr              3
+     * : additive.expr
+     * | shift.expr (LSHIFT | RSHIFT | ARSHIFT) additive.expr
+     *
+     * additive.expr           2
+     * : multiply.expr
+     * | additive.expr ("+" | "-") multiply.expr
+     *
+     * multiply.expr           1
+     * : unary.expr
+     * | multiply.expr ("*" | "/" | "%") unary.expr
+     */
+    private ASTree parseBinaryExpr(SymbolTable tbl) throws CompileError {
+       ASTree expr = parseUnaryExpr(tbl);
+       for (;;) {
+           int t = lex.lookAhead();
+           int p = getOpPrecedence(t);
+           if (p == 0)
+               return expr;
+           else
+               expr = binaryExpr2(tbl, expr, p);
+       }
+    }
+
+    private ASTree parseInstanceOf(SymbolTable tbl, ASTree expr)
+       throws CompileError
+    {
+       int t = lex.lookAhead();
+       if (isBuiltinType(t)) {
+           lex.get();  // primitive type
+           int dim = parseArrayDimension();
+           return new InstanceOfExpr(t, dim, expr);
+       }
+       else {
+           ASTList name = parseClassType(tbl);
+           int dim = parseArrayDimension();
+           return new InstanceOfExpr(name, dim, expr);
+       }
+    }
+
+    private ASTree binaryExpr2(SymbolTable tbl, ASTree expr, int prec)
+       throws CompileError
+    {
+       int t = lex.get();
+       if (t == INSTANCEOF)
+           return parseInstanceOf(tbl, expr);
+
+       ASTree expr2 = parseUnaryExpr(tbl);
+       for (;;) {
+           int t2 = lex.lookAhead();
+           int p2 = getOpPrecedence(t2);
+           if (p2 != 0 && prec > p2)
+               expr2 = binaryExpr2(tbl, expr2, p2);
+           else
+               return BinExpr.makeBin(t, expr, expr2);
+       }
+    }
+
+    // !"#$%&'(    )*+,-./0    12345678    9:;<=>?
+    private static final int[] binaryOpPrecedence
+       =  { 0, 0, 0, 0, 1, 6, 0, 0,
+            0, 1, 2, 0, 2, 0, 1, 0,
+            0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 4, 0, 4, 0 };
+
+    private int getOpPrecedence(int c) {
+       if ('!' <= c && c <= '?')
+           return binaryOpPrecedence[c - '!'];
+       else if (c == '^')
+           return 7;
+       else if (c == '|')
+           return 8;
+       else if (c == ANDAND)
+           return 9;
+       else if (c == OROR)
+           return 10;
+       else if (c == EQ || c == NEQ)
+           return 5;
+       else if (c == LE || c == GE || c == INSTANCEOF)
+           return 4;
+       else if (c == LSHIFT || c == RSHIFT || c == ARSHIFT)
+           return 3;
+       else
+           return 0;   // not a binary operator
+    }
+
+    /* unary.expr : "++"|"--" unary.expr
+                 | "+"|"-" unary.expr
+                 | "!"|"~" unary.expr
+                 | cast.expr
+                 | postfix.expr
+
+       unary.expr.not.plus.minus is a unary expression starting without
+       "+", "-", "++", or "--".
+     */
+    private ASTree parseUnaryExpr(SymbolTable tbl) throws CompileError {
+       int t;
+       switch (lex.lookAhead()) {
+       case '+' :
+       case '-' :
+       case PLUSPLUS :
+       case MINUSMINUS :
+       case '!' :
+       case '~' :
+           t = lex.get();
+           return new Expr(t, parseUnaryExpr(tbl));
+       case '(' :
+           return parseCast(tbl);
+       default :
+           return parsePostfix(tbl);
+       }
+    }
+
+    /* cast.expr : "(" builtin.type ("[" "]")* ")" unary.expr
+                | "(" class.type ("[" "]")* ")" unary.expr2
+
+       unary.expr2 is a unary.expr begining with "(", NULL, StringL,
+       Identifier, THIS, SUPER, or NEW.
+     */
+    private ASTree parseCast(SymbolTable tbl) throws CompileError {
+       int t = lex.lookAhead(1);
+       if (isBuiltinType(t)) {
+           lex.get();  // '('
+           lex.get();  // primitive type
+           int dim = parseArrayDimension();
+           if (lex.get() != ')')
+               throw new CompileError(") is missing", lex);
+
+           return new CastExpr(t, dim, parseUnaryExpr(tbl));
+       }
+       else if (t == Identifier && nextIsClassCast()) {
+           lex.get();  // '('
+           ASTList name = parseClassType(tbl);
+           int dim = parseArrayDimension();
+           if (lex.get() != ')')
+               throw new CompileError(") is missing", lex);
+
+           return new CastExpr(name, dim, parseUnaryExpr(tbl));
+       }
+       else
+           return parsePostfix(tbl);
+    }
+
+    private boolean nextIsClassCast() {
+       int i = nextIsClassType(1);
+       if (i < 0)
+           return false;
+
+       int t = lex.lookAhead(i);
+       if (t != ')')
+           return false;
+
+       t = lex.lookAhead(i + 1);
+       return t == '(' || t == NULL || t == StringL
+              || t == Identifier || t == THIS || t == SUPER || t == NEW
+              || t == TRUE || t == FALSE || t == LongConstant
+              || t == IntConstant || t == CharConstant
+              || t == DoubleConstant || t == FloatConstant;
+    }
+
+    private int nextIsClassType(int i) {
+       int t;
+       while (lex.lookAhead(++i) == '.')
+           if (lex.lookAhead(++i) != Identifier)
+               return -1;
+
+       while ((t = lex.lookAhead(i++)) == '[')
+           if (lex.lookAhead(i++) != ']')
+               return -1;
+
+       return i - 1;
+    }
+
+    /* array.dimension : [ "[" "]" ]*
+     */
+    private int parseArrayDimension() throws CompileError {
+       int arrayDim = 0;
+       while (lex.lookAhead() == '[') {
+           ++arrayDim;
+           lex.get();
+           if (lex.get() != ']')
+               throw new CompileError("] is missing", lex);
+       }
+
+       return arrayDim;
+    }
+
+    /* class.type : Identifier ( "." Identifier )*
+     */
+    private ASTList parseClassType(SymbolTable tbl) throws CompileError {
+       ASTList list = null;
+       for (;;) {
+           if (lex.get() != Identifier)
+               throw new SyntaxError(lex);
+
+           list = ASTList.append(list, new Symbol(lex.getString()));
+           if (lex.lookAhead() == '.')
+               lex.get();
+           else
+               break;
+       }
+
+       return list;
+    }
+
+    /* postfix.expr : number.literal
+     *             | primary.expr
+     *             | method.expr
+     *             | postfix.expr "++" | "--"
+     *             | postfix.expr "[" array.size "]"
+     *             | postfix.expr "." Identifier
+     *             | postfix.expr "#" Identifier
+     *
+     * "#" is not an operator of regular Java.  It separates
+     * a class name and a member name in an expression for static member
+     * access.  For example,
+     *     java.lang.Integer.toString(3)       in regular Java
+     * must be written like this:
+     *     java.lang.Integer#toString(3)       for this compiler.
+     */
+    private ASTree parsePostfix(SymbolTable tbl) throws CompileError {
+       int token = lex.lookAhead();
+       switch (token) {
+       case LongConstant :
+       case IntConstant :
+       case CharConstant :
+           lex.get();
+           return new IntConst(lex.getLong(), token);
+       case DoubleConstant :
+       case FloatConstant :
+           lex.get();
+           return new DoubleConst(lex.getDouble(), token);
+       default :
+           break;
+       }
+
+       String str;
+       ASTree index;
+       ASTree expr = parsePrimaryExpr(tbl);
+       int t;
+       while (true) {
+           switch (lex.lookAhead()) {
+           case '(' :
+               expr = parseMethodCall(tbl, expr);
+               break;
+           case '[' :
+               index = parseArrayIndex(tbl);
+               if (index == null)
+                   throw new SyntaxError(lex);
+
+               expr = Expr.make(ARRAY, expr, index);
+               break;
+           case PLUSPLUS :
+           case MINUSMINUS :
+               t = lex.get();
+               expr = Expr.make(t, null, expr);
+               break;
+           case '.' :
+               lex.get();
+               if (lex.get() != Identifier)
+                   throw new CompileError("missing member name", lex);
+
+               expr = Expr.make('.', expr, new Member(lex.getString()));
+               break;
+           case '#' :
+               lex.get();
+               t = lex.get();
+               if (t == CLASS)
+                   str = "class";
+               else if (t == Identifier)
+                   str = lex.getString();
+               else
+                   throw new CompileError("missing static member name", lex);
+
+               expr = Expr.make(MEMBER, toClassName(expr, null),
+                                new Member(str));
+               break;
+           default :
+               return expr;
+           }
+       }
+    }
+
+    /* method.call : method.expr "(" argument.list ")"
+     * method.expr : THIS | SUPER | Identifier
+     *            | postfix.expr "." Identifier
+     *            | postfix.expr "#" Identifier
+     */
+    private ASTree parseMethodCall(SymbolTable tbl, ASTree expr)
+       throws CompileError
+    {
+       if (expr instanceof Keyword) {
+           int token = ((Keyword)expr).get();
+           if (token != THIS && token != SUPER)
+               throw new SyntaxError(lex);
+       }
+       else if (expr instanceof Symbol)        // Identifier
+           ;
+       else if (expr instanceof Expr) {
+           int op = ((Expr)expr).getOperator();
+           if (op != '.' && op != MEMBER)
+               throw new SyntaxError(lex);
+       }
+
+       return Expr.make(CALL, expr, parseArgumentList(tbl));
+    }
+
+
+    private ASTList toClassName(ASTree name, ASTList tail)
+       throws CompileError
+    {
+       if (name instanceof Symbol)
+           return new ASTList(name, tail);
+       else if (name instanceof Expr) {
+           Expr expr = (Expr)name;
+           if (expr.getOperator() == '.')
+               return toClassName(expr.oprand1(),
+                                  new ASTList(expr.oprand2(), tail));
+       }
+
+       throw new CompileError("bad static member access", lex);
+    }
+
+    /* primary.expr : THIS | SUPER | TRUE | FALSE | NULL
+     *             | StringL
+     *             | Identifier
+     *             | NEW new.expr
+     *             | "(" expression ")"
+     *
+     * Identifier represents either a local variable name, a member name,
+     * or a class name.
+     */
+    private ASTree parsePrimaryExpr(SymbolTable tbl) throws CompileError {
+       int t;
+       String name;
+       Declarator decl;
+       ASTree expr;
+
+       switch (t = lex.get()) {
+       case THIS :
+       case SUPER :
+       case TRUE :
+       case FALSE :
+       case NULL :
+           return new Keyword(t);
+       case Identifier :
+           name = lex.getString();
+           decl = tbl.lookup(name);
+           if (decl == null)
+               return new Member(name);        // this or static member
+           else
+               return new Variable(name, decl); // local variable
+       case StringL :
+           return new StringL(lex.getString());
+       case NEW :
+           return parseNew(tbl);
+       case '(' :
+           expr = parseExpression(tbl);
+           if (lex.get() == ')')
+               return expr;
+           else
+               throw new CompileError(") is missing", lex);
+       default :
+           throw new SyntaxError(lex);
+       }
+    }
+
+    /* new.expr : class.type "(" argument.list ")"
+     *         | class.type     array.size [ array.initializer ]
+     *         | primitive.type array.size [ array.initializer ]
+     */
+    private NewExpr parseNew(SymbolTable tbl) throws CompileError {
+       ASTree init = null;
+       int t = lex.lookAhead();
+       if (isBuiltinType(t)) {
+           lex.get();
+           ASTList size = parseArraySize(tbl);
+           if (lex.lookAhead() == '{')
+               init = parseArrayInitializer(tbl);
+
+           return new NewExpr(t, size, init);
+       }
+       else if (t == Identifier) {
+           ASTList name = parseClassType(tbl);
+           t = lex.lookAhead();
+           if (t == '(') {
+               ASTList args = parseArgumentList(tbl);
+               return new NewExpr(name, args);
+           }
+           else if (t == '[') {
+               ASTList size = parseArraySize(tbl);
+               if (lex.lookAhead() == '{')
+                   init = parseArrayInitializer(tbl);
+
+               return NewExpr.makeObjectArray(name, size, init);
+           }
+       }
+
+       throw new SyntaxError(lex);
+    }
+
+    /* array.size : [ array.index ]*
+     */
+    private ASTList parseArraySize(SymbolTable tbl) throws CompileError {
+       ASTList list = null;
+       while (lex.lookAhead() == '[')
+           list = ASTList.append(list, parseArrayIndex(tbl));
+
+       return list;
+    }
+
+    /* array.index : "[" [ expression ] "]"
+     */
+    private ASTree parseArrayIndex(SymbolTable tbl) throws CompileError {
+       lex.get();      // '['
+       if (lex.lookAhead() == ']') {
+           lex.get();
+           return null;
+       }
+       else {
+           ASTree index = parseExpression(tbl);
+           if (lex.get() != ']')
+               throw new CompileError("] is missing", lex);
+
+           return index;
+       }
+    }
+
+    /* argument.list : "(" [ expression [ "," expression ]* ] ")"
+     */
+    private ASTList parseArgumentList(SymbolTable tbl) throws CompileError {
+       if (lex.get() != '(')
+           throw new CompileError("( is missing", lex);
+
+       ASTList list = null;
+       if (lex.lookAhead() != ')')
+           for (;;) {
+               list = ASTList.append(list, parseExpression(tbl));
+               if (lex.lookAhead() == ',')
+                   lex.get();
+               else
+                   break;
+           }
+
+       if (lex.get() != ')')
+           throw new CompileError(") is missing", lex);
+
+       return list;
+    }
+}
+
diff --git a/src/main/javassist/compiler/ProceedHandler.java b/src/main/javassist/compiler/ProceedHandler.java
new file mode 100644 (file)
index 0000000..a787223
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+import javassist.bytecode.Bytecode;
+import javassist.compiler.ast.ASTList;
+
+/**
+ * An interface to an object for implementing $proceed().
+ *
+ * @see javassist.compiler.JvstCodeGen#setProceedHandler(ProceedHandler, String)
+ * @see javassist.compiler.JvstCodeGen#atMethodCall(Expr)
+ */
+public interface ProceedHandler {
+    void doit(JvstCodeGen gen, Bytecode b, ASTList args)
+       throws CompileError;
+}
diff --git a/src/main/javassist/compiler/SymbolTable.java b/src/main/javassist/compiler/SymbolTable.java
new file mode 100644 (file)
index 0000000..be31881
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+import java.util.HashMap;
+import javassist.compiler.ast.Declarator;
+
+public final class SymbolTable extends HashMap {
+    private SymbolTable parent;
+
+    public SymbolTable() { this(null); }
+
+    public SymbolTable(SymbolTable p) {
+       super();
+       parent = p;
+    }
+
+    public SymbolTable getParent() { return parent; }
+
+    public Declarator lookup(String name) {
+       Declarator found = (Declarator)get(name);
+       if (found == null && parent != null)
+           return parent.lookup(name);
+       else
+           return found;
+    }
+
+    public void append(String name, Declarator value) {
+       put(name, value);
+    }
+}
diff --git a/src/main/javassist/compiler/SyntaxError.java b/src/main/javassist/compiler/SyntaxError.java
new file mode 100644 (file)
index 0000000..f4c06d8
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+public class SyntaxError extends CompileError {
+    public SyntaxError(Lex l) {
+       super("syntax error", l);
+    }
+}
diff --git a/src/main/javassist/compiler/TokenId.java b/src/main/javassist/compiler/TokenId.java
new file mode 100644 (file)
index 0000000..8b78bf0
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler;
+
+public interface TokenId {
+    int ABSTRACT = 300;
+    int BOOLEAN = 301;
+    int BREAK = 302;
+    int BYTE = 303;
+    int CASE = 304;
+    int CATCH = 305;
+    int CHAR = 306;
+    int CLASS = 307;
+    int CONST = 308;   // reserved keyword
+    int CONTINUE = 309;
+    int DEFAULT = 310;
+    int DO = 311;
+    int DOUBLE = 312;
+    int ELSE = 313;
+    int EXTENDS = 314;
+    int FINAL = 315;
+    int FINALLY = 316;
+    int FLOAT = 317;
+    int FOR = 318;
+    int GOTO = 319;    // reserved keyword
+    int IF = 320;
+    int IMPLEMENTS = 321;
+    int IMPORT = 322;
+    int INSTANCEOF = 323;
+    int INT = 324;
+    int INTERFACE = 325;
+    int LONG = 326;
+    int NATIVE = 327;
+    int NEW = 328;
+    int PACKAGE = 329;
+    int PRIVATE = 330;
+    int PROTECTED = 331;
+    int PUBLIC = 332;
+    int RETURN = 333;
+    int SHORT = 334;
+    int STATIC = 335;
+    int SUPER = 336;
+    int SWITCH = 337;
+    int SYNCHRONIZED = 338;
+    int THIS = 339;
+    int THROW = 340;
+    int THROWS = 341;
+    int TRANSIENT = 342;
+    int TRY = 343;
+    int VOID = 344;
+    int VOLATILE = 345;
+    int WHILE = 346;
+    int STRICT = 347;
+
+    int NEQ = 350;     // !=
+    int MOD_E = 351;   // %=
+    int AND_E = 352;   // &=
+    int MUL_E = 353;   // *=
+    int PLUS_E = 354;  // +=
+    int MINUS_E = 355; // -=
+    int DIV_E = 356;   // /=
+    int LE = 357;              // <=
+    int EQ = 358;              // ==
+    int GE = 359;              // >=
+    int EXOR_E = 360;  // ^=
+    int OR_E = 361;    // |=
+    int PLUSPLUS = 362;        // ++
+    int MINUSMINUS = 363;      // --
+    int LSHIFT = 364;  // <<
+    int LSHIFT_E = 365;        // <<=
+    int RSHIFT = 366;  // >>
+    int RSHIFT_E = 367;        // >>=
+    int OROR = 368;    // ||
+    int ANDAND = 369;  // &&
+    int ARSHIFT = 370; // >>>
+    int ARSHIFT_E = 371;       // >>>=
+
+    // operators from NEQ to ARSHIFT_E
+    String opNames[] = { "!=", "%=", "&=", "*=", "+=", "-=", "/=",
+                      "<=", "==", ">=", "^=", "|=", "++", "--",
+                      "<<", "<<=", ">>", ">>=", "||", "&&", ">>>",
+                      ">>>=" };
+
+    // operators from MOD_E to ARSHIFT_E
+    int assignOps[] = { '%', '&', '*', '+', '-', '/', 0, 0, 0,
+                       '^', '|', 0, 0, 0, LSHIFT, 0, RSHIFT, 0, 0, 0,
+                       ARSHIFT };
+
+    int Identifier = 400;
+    int CharConstant = 401;
+    int IntConstant = 402;
+    int LongConstant = 403;
+    int FloatConstant = 404;
+    int DoubleConstant = 405;
+    int StringL = 406;
+
+    int TRUE = 410;
+    int FALSE = 411;
+    int NULL = 412;
+
+    int CALL = 'C';    // method call
+    int ARRAY = 'A';   // array access
+    int MEMBER = '#';  // static member access
+
+    int EXPR = 'E';    // expression statement
+    int LABEL = 'L';   // label statement
+    int BLOCK = 'B';   // block statement
+    int DECL = 'D';    // declaration statement
+
+    int BadToken = 500;
+}
diff --git a/src/main/javassist/compiler/ast/ASTList.java b/src/main/javassist/compiler/ast/ASTList.java
new file mode 100644 (file)
index 0000000..887a528
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * A linked list.
+ * The right subtree must be an ASTList object or null.
+ */
+public class ASTList extends ASTree {
+    private ASTree left;
+    private ASTList right;
+
+    public ASTList(ASTree _head, ASTList _tail) {
+       left = _head;
+       right = _tail;
+    }
+
+    public ASTList(ASTree _head) {
+       left = _head;
+       right = null;
+    }
+
+    public static ASTList make(ASTree e1, ASTree e2, ASTree e3) {
+       return new ASTList(e1, new ASTList(e2, new ASTList(e3)));
+    }
+
+    public ASTree getLeft() { return left; }
+
+    public ASTree getRight() { return right; }
+
+    public void setLeft(ASTree _left) { left = _left; }
+
+    public void setRight(ASTree _right) {
+       right = (ASTList)_right;
+    }
+
+    /**
+     * Returns the car part of the list.
+     */
+    public ASTree head() { return left; }
+
+    public void setHead(ASTree _head) {
+       left = _head;
+    }
+
+    /**
+     * Returns the cdr part of the list.
+     */
+    public ASTList tail() { return right; }
+
+    public void setTail(ASTList _tail) {
+       right = _tail;
+    }
+
+    public void accept(Visitor v) throws CompileError { v.atASTList(this); }
+
+    public String toString() {
+       StringBuffer sbuf = new StringBuffer();
+       sbuf.append("(<");
+       sbuf.append(getTag());
+       sbuf.append('>');
+       ASTList list = this;
+       while (list != null) {
+           sbuf.append(' ');
+           ASTree a = list.left;
+           sbuf.append(a == null ? "<null>" : a.toString());
+           list = list.right;
+       }
+
+       sbuf.append(')');
+       return sbuf.toString();
+    }
+
+    /**
+     * Returns the number of the elements in this list.
+     */
+    public int length() {
+       return length(this);
+    }
+
+    public static int length(ASTList list) {
+       if (list == null)
+           return 0;
+
+       int n = 0;
+       while (list != null) {
+           list = list.right;
+           ++n;
+       }
+
+       return n;
+    }
+
+    /**
+     * Returns a sub list of the list.  The sub list begins with the
+     * n-th element of the list.
+     *
+     * @param nth      zero or more than zero.
+     */
+    public ASTList sublist(int nth) {
+       ASTList list = this;
+       while (nth-- > 0)
+           list = list.right;
+
+       return list;
+    }
+
+    /**
+     * Substitutes <code>newObj</code> for <code>oldObj</code> in the
+     * list.
+     */
+    public boolean subst(ASTree newObj, ASTree oldObj) {
+       for (ASTList list = this; list != null; list = list.right)
+           if (list.left == oldObj) {
+               list.left = newObj;
+               return true;
+           }
+
+       return false;
+    }
+
+    /**
+     * Appends an object to a list.
+     */
+    public static ASTList append(ASTList a, ASTree b) {
+       return concat(a, new ASTList(b));
+    }
+
+    /**
+     * Concatenates two lists.
+     */
+    public static ASTList concat(ASTList a, ASTList b) {
+       if (a == null)
+           return b;
+       else {
+           ASTList list = a;
+           while (list.right != null)
+               list = list.right;
+
+           list.right = b;
+           return a;
+       }
+    }
+}
diff --git a/src/main/javassist/compiler/ast/ASTree.java b/src/main/javassist/compiler/ast/ASTree.java
new file mode 100644 (file)
index 0000000..7282514
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import java.io.Serializable;
+import javassist.compiler.CompileError;
+
+/**
+ * Abstract Syntax Tree.  An ASTree object represents a node of
+ * a binary tree.  If the node is a leaf node, both <code>getLeft()</code>
+ * and <code>getRight()</code> returns null.
+ */
+public abstract class ASTree implements Serializable {
+    public ASTree getLeft() { return null; }
+
+    public ASTree getRight() { return null; }
+
+    public void setLeft(ASTree _left) {}
+
+    public void setRight(ASTree _right) {}
+
+    /**
+     * Is a method for the visitor pattern.  It calls
+     * <code>atXXX()</code> on the given visitor, where
+     * <code>XXX</code> is the class name of the node object.
+     */
+    public abstract void accept(Visitor v) throws CompileError;
+
+    public String toString() {
+       StringBuffer sbuf = new StringBuffer();
+       sbuf.append('<');
+       sbuf.append(getTag());
+       sbuf.append('>');
+       return sbuf.toString();
+    }
+
+    /**
+     * Returns the type of this node.  This method is used by
+     * <code>toString()</code>.
+     */
+    protected String getTag() {
+       String name = getClass().getName();
+       return name.substring(name.lastIndexOf('.') + 1);
+    }
+}
diff --git a/src/main/javassist/compiler/ast/AssignExpr.java b/src/main/javassist/compiler/ast/AssignExpr.java
new file mode 100644 (file)
index 0000000..0a062d4
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Assignment expression.
+ */
+public class AssignExpr extends Expr {
+    /* operator must be either of:
+     * =, %=, &=, *=, +=, -=, /=, ^=, |=, <<=, >>=, >>>=
+     */
+
+    public AssignExpr(int op, ASTree _head, ASTList _tail) {
+       super(op, _head, _tail);
+    }
+
+    public static AssignExpr makeAssign(int op, ASTree oprand1,
+                                       ASTree oprand2) {
+       return new AssignExpr(op, oprand1, new ASTList(oprand2));
+    }
+
+    public void accept(Visitor v) throws CompileError {
+       v.atAssignExpr(this);
+    }
+}
diff --git a/src/main/javassist/compiler/ast/BinExpr.java b/src/main/javassist/compiler/ast/BinExpr.java
new file mode 100644 (file)
index 0000000..a8fdeb6
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Binary expression.
+ */
+public class BinExpr extends Expr {
+    /* operator must be either of:
+     * ||, &&, |, ^, &, ==, !=, <=, >=, <, >,
+     * <<, >>, >>>, +, -, *, /, %
+     */
+
+    public BinExpr(int op, ASTree _head, ASTList _tail) {
+       super(op, _head, _tail);
+    }
+
+    public static BinExpr makeBin(int op, ASTree oprand1, ASTree oprand2) {
+       return new BinExpr(op, oprand1, new ASTList(oprand2));
+    }
+
+    public void accept(Visitor v) throws CompileError { v.atBinExpr(this); }
+}
diff --git a/src/main/javassist/compiler/ast/CastExpr.java b/src/main/javassist/compiler/ast/CastExpr.java
new file mode 100644 (file)
index 0000000..7f8775c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.TokenId;
+import javassist.compiler.CompileError;
+
+/**
+ * Cast expression.
+ */
+public class CastExpr extends ASTList implements TokenId {
+    protected int castType;
+    protected int arrayDim;
+
+    public CastExpr(ASTList className, int dim, ASTree expr) {
+       super(className, new ASTList(expr));
+       castType = CLASS;
+       arrayDim = dim;
+    }
+
+    public CastExpr(int type, int dim, ASTree expr) {
+       super(null, new ASTList(expr));
+       castType = type;
+       arrayDim = dim;
+    }
+
+    /* Returns CLASS, BOOLEAN, INT, or ...
+     */
+    public int getType() { return castType; }
+
+    public int getArrayDim() { return arrayDim; }
+
+    public ASTList getClassName() { return (ASTList)getLeft(); }
+
+    public ASTree getOprand() { return getRight().getLeft(); }
+
+    public String getTag() { return "cast:" + castType + ":" + arrayDim; }
+
+    public void accept(Visitor v) throws CompileError { v.atCastExpr(this); }
+}
diff --git a/src/main/javassist/compiler/ast/CondExpr.java b/src/main/javassist/compiler/ast/CondExpr.java
new file mode 100644 (file)
index 0000000..d4a8a10
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Conditional expression.
+ */
+public class CondExpr extends ASTList {
+    public CondExpr(ASTree cond, ASTree thenp, ASTree elsep) {
+       super(cond, new ASTList(thenp, new ASTList(elsep)));
+    }
+
+    public ASTree condExpr() { return head(); }
+
+    public ASTree thenExpr() { return tail().head(); }
+
+    public ASTree elseExpr() { return tail().tail().head(); }
+
+    public String getTag() { return "?:"; }
+
+    public void accept(Visitor v) throws CompileError { v.atCondExpr(this); }
+}
diff --git a/src/main/javassist/compiler/ast/Declarator.java b/src/main/javassist/compiler/ast/Declarator.java
new file mode 100644 (file)
index 0000000..edeb839
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.TokenId;
+import javassist.compiler.CompileError;
+
+/**
+ * Variable declarator.
+ */
+public class Declarator extends ASTList implements TokenId {
+    protected int varType;
+    protected int arrayDim;
+    protected int localVar;
+    protected String qualifiedClass;   // JVM-internal representation
+
+    public Declarator(int type, int dim) {
+       super(null);
+       varType = type;
+       arrayDim = dim;
+       localVar = -1;
+       qualifiedClass = null;
+    }
+
+    public Declarator(ASTList className, int dim) {
+       super(null);
+       varType = CLASS;
+       arrayDim = dim;
+       localVar = -1;
+       qualifiedClass = astToClassName(className, '/');
+    }
+
+    /* For declaring a pre-defined? local variable.
+     */
+    public Declarator(int type, String jvmClassName, int dim,
+                     int var, Symbol sym) {
+       super(null);
+       varType = type;
+       arrayDim = dim;
+       localVar = var;
+       qualifiedClass = jvmClassName;
+       setLeft(sym);
+       append(this, null);     // initializer
+    }
+
+    public Declarator make(Symbol sym, int dim, ASTree init) {
+       Declarator d = new Declarator(this.varType, this.arrayDim + dim);
+       d.qualifiedClass = this.qualifiedClass;
+       d.setLeft(sym);
+       d.append(d, init);
+       return d;
+    }
+
+    /* Returns CLASS, BOOLEAN, BYTE, CHAR, SHORT, INT, LONG, FLOAT,
+     * or DOUBLE (or VOID)
+     */
+    public int getType() { return varType; }
+
+    public int getArrayDim() { return arrayDim; }
+
+    public void addArrayDim(int d) { arrayDim += d; }
+
+    public String getClassName() { return qualifiedClass; }
+
+    public void setClassName(String s) { qualifiedClass = s; }
+
+    public Symbol getVariable() { return (Symbol)getLeft(); }
+
+    public void setVariable(Symbol sym) { setLeft(sym); }
+
+    public ASTree getInitializer() {
+       ASTList t = tail();
+       if (t != null)
+           return t.head();
+       else
+           return null;
+    }
+
+    public void setLocalVar(int n) { localVar = n; }
+
+    public int getLocalVar() { return localVar; }
+
+    public String getTag() { return "decl"; }
+
+    public void accept(Visitor v) throws CompileError {
+       v.atDeclarator(this);
+    }
+
+    public static String astToClassName(ASTList name, char sep) {
+       if (name == null)
+           return null;
+
+       StringBuffer sbuf = new StringBuffer();
+       for (;;) {
+           sbuf.append(((Symbol)name.head()).get());
+           name = name.tail();
+           if (name == null)
+               break;
+
+           sbuf.append(sep);
+       }
+
+       return sbuf.toString();
+    }
+}
diff --git a/src/main/javassist/compiler/ast/DoubleConst.java b/src/main/javassist/compiler/ast/DoubleConst.java
new file mode 100644 (file)
index 0000000..459f030
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Double constant.
+ */
+public class DoubleConst extends ASTree {
+    protected double value;
+    protected int type;
+
+    public DoubleConst(double v, int tokenId) { value = v; type = tokenId; }
+
+    public double get() { return value; }
+
+    /* Returns DoubleConstant or FloatConstant
+     */
+    public int getType() { return type; }
+
+    public String toString() { return Double.toString(value); }
+
+    public void accept(Visitor v) throws CompileError {
+       v.atDoubleConst(this);
+    }
+}
diff --git a/src/main/javassist/compiler/ast/Expr.java b/src/main/javassist/compiler/ast/Expr.java
new file mode 100644 (file)
index 0000000..1fecc9c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.TokenId;
+import javassist.compiler.CompileError;
+
+/**
+ * Expression.
+ */
+public class Expr extends ASTList implements TokenId {
+    /* operator must be either of:
+     * (unary) +, (unary) -, ++, --, !, ~,
+     * CALL, ARRAY, . (dot), MEMBER (static member access).
+     * Otherwise, the object should be an instance of a subclass.
+     */
+
+    protected int operatorId;
+
+    public Expr(int op, ASTree _head, ASTList _tail) {
+       super(_head, _tail);
+       operatorId = op;
+    }
+
+    public Expr(int op, ASTree _head) {
+       super(_head);
+       operatorId = op;
+    }
+
+    public static Expr make(int op, ASTree oprand1, ASTree oprand2) {
+       return new Expr(op, oprand1, new ASTList(oprand2));
+    }
+
+    public int getOperator() { return operatorId; }
+
+    public ASTree oprand1() { return getLeft(); }
+
+    public ASTree oprand2() { return getRight().getLeft(); }
+
+    public void accept(Visitor v) throws CompileError { v.atExpr(this); }
+
+    public String getName() {
+       int id = operatorId;
+       if (id < 128)
+           return String.valueOf((char)id);
+       else if (NEQ <= id && id <= ARSHIFT_E)
+           return opNames[id - NEQ];
+       else if (id == INSTANCEOF)
+           return "instanceof";
+       else
+           return String.valueOf(id);
+    }
+
+    protected String getTag() {
+       return "op:" + getName();
+    }
+}
diff --git a/src/main/javassist/compiler/ast/FieldDecl.java b/src/main/javassist/compiler/ast/FieldDecl.java
new file mode 100644 (file)
index 0000000..d57dd63
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+public class FieldDecl extends ASTList {
+    public FieldDecl(ASTree _head, ASTList _tail) {
+       super(_head, _tail);
+    }
+
+    public ASTList getModifiers() { return (ASTList)getLeft(); }
+
+    public Declarator getDeclarator() { return (Declarator)tail().head(); }
+
+    public ASTree getInit() { return (ASTree)sublist(2).head(); }
+
+    public void accept(Visitor v) throws CompileError {
+       v.atFieldDecl(this);
+    }
+}
diff --git a/src/main/javassist/compiler/ast/InstanceOfExpr.java b/src/main/javassist/compiler/ast/InstanceOfExpr.java
new file mode 100644 (file)
index 0000000..03d6bc1
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Instanceof expression.
+ */
+public class InstanceOfExpr extends CastExpr {
+    public InstanceOfExpr(ASTList className, int dim, ASTree expr) {
+       super(className, dim, expr);
+    }
+
+    public InstanceOfExpr(int type, int dim, ASTree expr) {
+       super(type, dim, expr);
+    }
+
+    public String getTag() {
+       return "instanceof:" + castType + ":" + arrayDim;
+    }
+
+    public void accept(Visitor v) throws CompileError {
+       v.atInstanceOfExpr(this);
+    }
+}
diff --git a/src/main/javassist/compiler/ast/IntConst.java b/src/main/javassist/compiler/ast/IntConst.java
new file mode 100644 (file)
index 0000000..63ce70c
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Integer constant.
+ */
+public class IntConst extends ASTree {
+    protected long value;
+    protected int type;
+
+    public IntConst(long v, int tokenId) { value = v; type = tokenId; }
+
+    public long get() { return value; }
+
+    /* Returns IntConstant, CharConstant, or LongConstant.
+     */
+    public int getType() { return type; }
+
+    public String toString() { return Long.toString(value); }
+
+    public void accept(Visitor v) throws CompileError {
+       v.atIntConst(this);
+    }
+}
diff --git a/src/main/javassist/compiler/ast/Keyword.java b/src/main/javassist/compiler/ast/Keyword.java
new file mode 100644 (file)
index 0000000..9e1798e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Keyword.
+ */
+public class Keyword extends ASTree {
+    protected int tokenId;
+
+    public Keyword(int token) {
+       tokenId = token;
+    }
+
+    public int get() { return tokenId; }
+
+    public String toString() { return "id:" + tokenId; }
+
+    public void accept(Visitor v) throws CompileError { v.atKeyword(this); }
+}
diff --git a/src/main/javassist/compiler/ast/Member.java b/src/main/javassist/compiler/ast/Member.java
new file mode 100644 (file)
index 0000000..a482587
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Member name.
+ */
+public class Member extends Symbol {
+    public Member(String name) {
+       super(name);
+    }
+
+    public void accept(Visitor v) throws CompileError { v.atMember(this); }
+}
diff --git a/src/main/javassist/compiler/ast/MethodDecl.java b/src/main/javassist/compiler/ast/MethodDecl.java
new file mode 100644 (file)
index 0000000..3c52901
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+public class MethodDecl extends ASTList {
+    public static final String initName = "<init>";
+
+    public MethodDecl(ASTree _head, ASTList _tail) {
+       super(_head, _tail);
+    }
+
+    public boolean isConstructor() {
+       Symbol sym = getReturn().getVariable();
+       return sym != null && initName.equals(sym.get());
+    }
+
+    public ASTList getModifiers() { return (ASTList)getLeft(); }
+
+    public Declarator getReturn() { return (Declarator)tail().head(); }
+
+    public ASTList getParams() { return (ASTList)sublist(2).head(); }
+
+    public ASTList getThrows() { return (ASTList)sublist(3).head(); }
+
+    public Stmnt getBody() { return (Stmnt)sublist(4).head(); }
+
+    public void accept(Visitor v) throws CompileError {
+       v.atMethodDecl(this);
+    }
+}
diff --git a/src/main/javassist/compiler/ast/NewExpr.java b/src/main/javassist/compiler/ast/NewExpr.java
new file mode 100644 (file)
index 0000000..33c721e
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.TokenId;
+import javassist.compiler.CompileError;
+
+/**
+ * New Expression.
+ */
+public class NewExpr extends ASTList implements TokenId {
+    protected boolean newArray;
+    protected int arrayType;
+
+    public NewExpr(ASTList className, ASTList args) {
+       super(className, new ASTList(args));
+       newArray = false;
+       arrayType = CLASS;
+    }
+
+    public NewExpr(int type, ASTList arraySize, ASTree init) {
+       super(null, new ASTList(arraySize));
+       newArray = true;
+       arrayType = type;
+       if (init != null)
+           append(this, init);
+    }
+
+    public static NewExpr makeObjectArray(ASTList className,
+                                         ASTList arraySize, ASTree init) {
+       NewExpr e = new NewExpr(className, arraySize);
+       e.newArray = true;
+       if (init != null)
+           append(e, init);
+
+       return e;
+    }
+
+    public boolean isArray() { return newArray; }
+
+    /* TokenId.CLASS, TokenId.INT, ...
+     */
+    public int getArrayType() { return arrayType; }
+
+    public ASTList getClassName() { return (ASTList)getLeft(); }
+
+    public ASTList getArguments() { return (ASTList)getRight().getLeft(); }
+
+    public ASTList getArraySize() { return getArguments(); }
+
+    public ASTree getInitializer() {
+       ASTree t = getRight().getRight();
+       if (t == null)
+           return null;
+       else
+           return t.getLeft();
+    }
+
+    public void accept(Visitor v) throws CompileError { v.atNewExpr(this); }
+
+    protected String getTag() {
+       return newArray ? "new[]" : "new";
+    }
+}
diff --git a/src/main/javassist/compiler/ast/Pair.java b/src/main/javassist/compiler/ast/Pair.java
new file mode 100644 (file)
index 0000000..520207a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * A node of a a binary tree.  This class provides concrete methods
+ * overriding abstract methods in ASTree.
+ */
+public class Pair extends ASTree {
+    protected ASTree left, right;
+
+    public Pair(ASTree _left, ASTree _right) {
+       left = _left;
+       right = _right;
+    }
+
+    public void accept(Visitor v) throws CompileError { v.atPair(this); }
+
+    public String toString() {
+       StringBuffer sbuf = new StringBuffer();
+       sbuf.append("(<Pair> ");
+       sbuf.append(left == null ? "<null>" : left.toString());
+       sbuf.append(" . ");
+       sbuf.append(right == null ? "<null>" : right.toString());
+       sbuf.append(')');
+       return sbuf.toString();
+    }
+
+    public ASTree getLeft() { return left; }
+
+    public ASTree getRight() { return right; }
+
+    public void setLeft(ASTree _left) { left = _left; }
+
+    public void setRight(ASTree _right) { right = _right; }
+}
diff --git a/src/main/javassist/compiler/ast/Stmnt.java b/src/main/javassist/compiler/ast/Stmnt.java
new file mode 100644 (file)
index 0000000..c6b9a97
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.TokenId;
+import javassist.compiler.CompileError;
+
+/**
+ * Statement.
+ */
+public class Stmnt extends ASTList implements TokenId {
+    protected int operatorId;
+
+    public Stmnt(int op, ASTree _head, ASTList _tail) {
+       super(_head, _tail);
+       operatorId = op;
+    }
+
+    public Stmnt(int op, ASTree _head) {
+       super(_head);
+       operatorId = op;
+    }
+
+    public Stmnt(int op) {
+       this(op, null);
+    }
+
+    public static Stmnt make(int op, ASTree oprand1, ASTree oprand2) {
+       return new Stmnt(op, oprand1, new ASTList(oprand2));
+    }
+
+    public static Stmnt make(int op, ASTree op1, ASTree op2, ASTree op3) {
+       return new Stmnt(op, op1, new ASTList(op2, new ASTList(op3)));
+    }
+
+    public void accept(Visitor v) throws CompileError { v.atStmnt(this); }
+
+    public int getOperator() { return operatorId; }
+
+    protected String getTag() {
+       if (operatorId < 128)
+           return "stmnt:" + (char)operatorId;
+       else
+           return "stmnt:" + operatorId;
+    }
+}
diff --git a/src/main/javassist/compiler/ast/StringL.java b/src/main/javassist/compiler/ast/StringL.java
new file mode 100644 (file)
index 0000000..5f173f5
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * String literal.
+ */
+public class StringL extends ASTree {
+    protected String text;
+
+    public StringL(String t) {
+       text = t;
+    }
+
+    public String get() { return text; }
+
+    public String toString() { return "\"" + text + "\""; }
+
+    public void accept(Visitor v) throws CompileError { v.atStringL(this); }
+}
diff --git a/src/main/javassist/compiler/ast/Symbol.java b/src/main/javassist/compiler/ast/Symbol.java
new file mode 100644 (file)
index 0000000..c8e753b
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Identifier.
+ */
+public class Symbol extends ASTree {
+    protected String identifier;
+
+    public Symbol(String sym) {
+       identifier = sym;
+    }
+
+    public String get() { return identifier; }
+
+    public String toString() { return identifier; }
+
+    public void accept(Visitor v) throws CompileError { v.atSymbol(this); }
+}
diff --git a/src/main/javassist/compiler/ast/Variable.java b/src/main/javassist/compiler/ast/Variable.java
new file mode 100644 (file)
index 0000000..16112a9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * Variable.
+ */
+public class Variable extends Symbol {
+    protected Declarator declarator;
+
+    public Variable(String sym, Declarator d) {
+       super(sym);
+       declarator = d;
+    }
+
+    public Declarator getDeclarator() { return declarator; }
+
+    public String toString() {
+       return identifier + ":" + declarator.getType();
+    }
+
+    public void accept(Visitor v) throws CompileError { v.atVariable(this); }
+}
diff --git a/src/main/javassist/compiler/ast/Visitor.java b/src/main/javassist/compiler/ast/Visitor.java
new file mode 100644 (file)
index 0000000..685713d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.compiler.ast;
+
+import javassist.compiler.CompileError;
+
+/**
+ * The visitor pattern.
+ *
+ * @see ast.ASTree#accept(Visitor)
+ */
+public class Visitor {
+    public void atASTList(ASTList n) throws CompileError {}
+    public void atPair(Pair n) throws CompileError {}
+
+    public void atFieldDecl(FieldDecl n) throws CompileError {}
+    public void atMethodDecl(MethodDecl n) throws CompileError {}
+    public void atStmnt(Stmnt n) throws CompileError {}
+    public void atDeclarator(Declarator n) throws CompileError {}
+
+    public void atAssignExpr(AssignExpr n) throws CompileError {}
+    public void atCondExpr(CondExpr n) throws CompileError {}
+    public void atBinExpr(BinExpr n) throws CompileError {}
+    public void atExpr(Expr n) throws CompileError {}
+    public void atCastExpr(CastExpr n) throws CompileError {}
+    public void atInstanceOfExpr(InstanceOfExpr n) throws CompileError {}
+    public void atNewExpr(NewExpr n) throws CompileError {}
+
+    public void atSymbol(Symbol n) throws CompileError {}
+    public void atMember(Member n) throws CompileError {}
+    public void atVariable(Variable n) throws CompileError {}
+    public void atKeyword(Keyword n) throws CompileError {}
+    public void atStringL(StringL n) throws CompileError {}
+    public void atIntConst(IntConst n) throws CompileError {}
+    public void atDoubleConst(DoubleConst n) throws CompileError {}
+}
diff --git a/src/main/javassist/convert/TransformAfter.java b/src/main/javassist/convert/TransformAfter.java
new file mode 100644 (file)
index 0000000..e970434
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.convert;
+
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.*;
+import javassist.CannotCompileException;
+
+public class TransformAfter extends TransformBefore {
+    public TransformAfter(Transformer next,
+                          CtMethod origMethod, CtMethod afterMethod)
+       throws NotFoundException
+    {
+       super(next, origMethod, afterMethod);
+    }
+
+    protected int match2(int pos, CodeIterator iterator) throws BadBytecode {
+       iterator.move(pos);
+       iterator.insert(saveCode);
+       iterator.insert(loadCode);
+       int p = iterator.insertGap(3);
+       iterator.insert(loadCode);
+       pos = iterator.next();
+       iterator.writeByte(iterator.byteAt(pos), p);
+       iterator.write16bit(iterator.u16bitAt(pos + 1), p + 1);
+       iterator.writeByte(INVOKESTATIC, pos);
+       iterator.write16bit(newIndex, pos + 1);
+       iterator.move(p);
+       return iterator.next();
+    }
+}
diff --git a/src/main/javassist/convert/TransformBefore.java b/src/main/javassist/convert/TransformBefore.java
new file mode 100644 (file)
index 0000000..79deb10
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.convert;
+
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.*;
+import javassist.CannotCompileException;
+
+public class TransformBefore extends TransformCall {
+    protected CtClass[] parameterTypes;
+    protected int locals;
+    protected int maxLocals;
+    protected byte[] saveCode, loadCode;
+
+    public TransformBefore(Transformer next,
+                          CtMethod origMethod, CtMethod beforeMethod)
+       throws NotFoundException
+    {
+       super(next, origMethod, beforeMethod);
+       parameterTypes = origMethod.getParameterTypes();
+       locals = 0;
+       maxLocals = 0;
+       saveCode = loadCode = null;
+    }
+
+    public void initialize(ConstPool cp, CodeAttribute attr) {
+       super.initialize(cp, attr);
+       locals = 0;
+       maxLocals = attr.getMaxLocals();
+       saveCode = loadCode = null;
+    }
+
+    protected int match(int c, int pos, CodeIterator iterator,
+                       int typedesc, ConstPool cp) throws BadBytecode
+    {
+       if (newIndex == 0) {
+           String desc = Descriptor.ofParameters(parameterTypes) + 'V';
+           desc = Descriptor.insertParameter(classname, desc);
+           int nt = cp.addNameAndTypeInfo(newMethodname, desc);
+           int ci = cp.addClassInfo(newClassname);
+           newIndex = cp.addMethodrefInfo(ci, nt);
+           constPool = cp;
+       }
+
+       if (saveCode == null)
+           makeCode(parameterTypes, cp);
+
+       return match2(pos, iterator);
+    }
+
+    protected int match2(int pos, CodeIterator iterator) throws BadBytecode {
+       iterator.move(pos);
+       iterator.insert(saveCode);
+       iterator.insert(loadCode);
+       int p = iterator.insertGap(3);
+       iterator.writeByte(INVOKESTATIC, p);
+       iterator.write16bit(newIndex, p + 1);
+       iterator.insert(loadCode);
+       return iterator.next();
+    }
+
+    public int extraLocals() { return locals; }
+
+    protected void makeCode(CtClass[] paramTypes, ConstPool cp) {
+       Bytecode save = new Bytecode(cp, 0, 0);
+       Bytecode load = new Bytecode(cp, 0, 0);
+
+       int var = maxLocals;
+       int len = (paramTypes == null) ? 0 : paramTypes.length;
+       load.addAload(var);
+       makeCode2(save, load, 0, len, paramTypes, var + 1);
+       save.addAstore(var);
+
+       saveCode = save.get();
+       loadCode = load.get();
+    }
+
+    private void makeCode2(Bytecode save, Bytecode load,
+                          int i, int n, CtClass[] paramTypes, int var)
+    {
+       if (i < n) {
+           int size = load.addLoad(var, paramTypes[i]);
+           makeCode2(save, load, i + 1, n, paramTypes, var + size);
+           save.addStore(var, paramTypes[i]);
+       }
+       else
+           locals = var - maxLocals;
+    }
+}
diff --git a/src/main/javassist/convert/TransformCall.java b/src/main/javassist/convert/TransformCall.java
new file mode 100644 (file)
index 0000000..985a8bb
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.convert;
+
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.bytecode.*;
+import javassist.CannotCompileException;
+
+public class TransformCall extends Transformer {
+    protected String classname, methodname, methodDescriptor;
+    protected String newClassname, newMethodname;
+
+    /* cache */
+    protected int newIndex;
+    protected ConstPool constPool;
+
+    public TransformCall(Transformer next, CtMethod origMethod,
+                        CtMethod substMethod)
+    {
+       super(next);
+       this.classname = origMethod.getDeclaringClass().getName();
+       this.methodname = origMethod.getName();
+       this.methodDescriptor = origMethod.getMethodInfo2().getDescriptor();
+       this.newClassname = substMethod.getDeclaringClass().getName();
+       this.newMethodname = substMethod.getName();
+       this.constPool = null;
+    }
+
+    public void initialize(ConstPool cp, CodeAttribute attr) {
+       if (constPool != cp)
+           newIndex = 0;
+    }
+
+    /**
+     * Modify INVOKEINTERFACE, INVOKESPECIAL, INVOKESTATIC and INVOKEVIRTUAL
+     * so that a different method is invoked.
+     */
+    public int transform(CtClass clazz, int pos, CodeIterator iterator,
+                        ConstPool cp) throws BadBytecode
+    {
+       int c = iterator.byteAt(pos);
+       if (c == INVOKEINTERFACE || c == INVOKESPECIAL
+                       || c == INVOKESTATIC || c == INVOKEVIRTUAL) {
+           int index = iterator.u16bitAt(pos + 1);
+           int typedesc = cp.isMember(classname, methodname, index);
+           if (typedesc != 0)
+               if (cp.getUtf8Info(typedesc).equals(methodDescriptor))
+                   pos = match(c, pos, iterator, typedesc, cp);
+       }
+
+       return pos;
+    }
+
+    protected int match(int c, int pos, CodeIterator iterator,
+                       int typedesc, ConstPool cp) throws BadBytecode
+    {
+       if (newIndex == 0) {
+           int nt = cp.addNameAndTypeInfo(cp.addUtf8Info(newMethodname),
+                                          typedesc);
+           int ci = cp.addClassInfo(newClassname);
+           if (c == INVOKEINTERFACE)
+               newIndex = cp.addInterfaceMethodrefInfo(ci, nt);
+           else
+               newIndex = cp.addMethodrefInfo(ci, nt);
+
+           constPool = cp;
+       }
+
+       iterator.write16bit(newIndex, pos + 1);
+       return pos;
+    }
+}
diff --git a/src/main/javassist/convert/TransformFieldAccess.java b/src/main/javassist/convert/TransformFieldAccess.java
new file mode 100644 (file)
index 0000000..bce010d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.convert;
+
+import javassist.bytecode.*;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.Modifier;
+import javassist.CannotCompileException;
+
+final public class TransformFieldAccess extends Transformer {
+    private String newClassname, newFieldname;
+    private String fieldname;
+    private CtClass fieldClass;
+    private boolean isPrivate;
+
+    /* cache */
+    private int newIndex;
+    private ConstPool constPool;
+
+    public TransformFieldAccess(Transformer next, CtField field,
+                               String newClassname, String newFieldname)
+    {
+       super(next);
+       this.fieldClass = field.getDeclaringClass();
+       this.fieldname = field.getName();
+       this.isPrivate = Modifier.isPrivate(field.getModifiers());
+       this.newClassname = newClassname;
+       this.newFieldname = newFieldname;
+       this.constPool = null;
+    }
+
+    public void initialize(ConstPool cp, CodeAttribute attr) {
+       if (constPool != cp)
+           newIndex = 0;
+    }
+
+    /**
+     * Modify GETFIELD, GETSTATIC, PUTFIELD, and PUTSTATIC so that
+     * a different field is accessed.  The new field must be declared
+     * in a superclass of the class in which the original field is
+     * declared.
+     */
+    public int transform(CtClass clazz, int pos,
+                        CodeIterator iterator, ConstPool cp)
+    {
+       int c = iterator.byteAt(pos);
+       if (c == GETFIELD || c == GETSTATIC
+                               || c == PUTFIELD || c == PUTSTATIC) {
+           int index = iterator.u16bitAt(pos + 1);
+           String typedesc
+               = TransformReadField.isField(clazz.getClassPool(), cp,
+                               fieldClass, fieldname, isPrivate, index);
+           if (typedesc != null) {
+               if (newIndex == 0) {
+                   int nt = cp.addNameAndTypeInfo(newFieldname,
+                                                  typedesc);
+                   newIndex = cp.addFieldrefInfo(
+                                       cp.addClassInfo(newClassname), nt);
+                   constPool = cp;
+               }
+
+               iterator.write16bit(newIndex, pos + 1);
+           }
+       }
+
+       return pos;
+    }
+}
diff --git a/src/main/javassist/convert/TransformNew.java b/src/main/javassist/convert/TransformNew.java
new file mode 100644 (file)
index 0000000..a69918d
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.convert;
+
+import javassist.bytecode.*;
+import javassist.CtClass;
+import javassist.CannotCompileException;
+
+final public class TransformNew extends Transformer {
+    private int nested;
+    private String classname, trapClass, trapMethod;
+
+    public TransformNew(Transformer next,
+                String classname, String trapClass, String trapMethod) {
+       super(next);
+       this.classname = classname;
+       this.trapClass = trapClass;
+       this.trapMethod = trapMethod;
+    }
+
+    public void initialize(ConstPool cp, CodeAttribute attr) {
+       nested = 0;
+    }
+
+    /**
+     * Replace a sequence of
+     *    NEW classname
+     *    DUP
+     *    ...
+     *    INVOKESPECIAL
+     * with
+     *    NOP
+     *    NOP
+     *    ...
+     *    INVOKESTATIC trapMethod in trapClass
+     */
+    public int transform(CtClass clazz, int pos, CodeIterator iterator,
+                        ConstPool cp) throws CannotCompileException
+    {
+       int index;
+       int c = iterator.byteAt(pos);
+       if (c == NEW) {
+           index = iterator.u16bitAt(pos + 1);
+           if (cp.getClassInfo(index).equals(classname)) {
+               if (iterator.byteAt(pos + 3) != DUP)
+                   throw new CannotCompileException(
+                               "NEW followed by no DUP was found");
+
+               iterator.writeByte(NOP, pos);
+               iterator.writeByte(NOP, pos + 1);
+               iterator.writeByte(NOP, pos + 2);
+               iterator.writeByte(NOP, pos + 3);
+               ++nested;
+           }
+       }
+       else if (c == INVOKESPECIAL) {
+           index = iterator.u16bitAt(pos + 1);
+           int typedesc = cp.isConstructor(classname, index);
+           if (typedesc != 0 && nested > 0) {
+               int methodref = computeMethodref(typedesc, cp);
+               iterator.writeByte(INVOKESTATIC, pos);
+               iterator.write16bit(methodref, pos + 1);
+               --nested;
+           }
+       }
+
+       return pos;
+    }
+
+    private int computeMethodref(int typedesc, ConstPool cp) {
+       int classIndex = cp.addClassInfo(trapClass);
+       int mnameIndex = cp.addUtf8Info(trapMethod);
+       typedesc = cp.addUtf8Info(
+               Descriptor.changeReturnType(classname,
+                                           cp.getUtf8Info(typedesc)));
+       return cp.addMethodrefInfo(classIndex,
+                       cp.addNameAndTypeInfo(mnameIndex, typedesc));
+    }
+}
diff --git a/src/main/javassist/convert/TransformReadField.java b/src/main/javassist/convert/TransformReadField.java
new file mode 100644 (file)
index 0000000..fda192c
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.convert;
+
+import javassist.bytecode.*;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CannotCompileException;
+import javassist.NotFoundException;
+import javassist.Modifier;
+
+public class TransformReadField extends Transformer {
+    protected String fieldname;
+    protected CtClass fieldClass;
+    protected boolean isPrivate;
+    protected String methodClassname, methodName;
+
+    public TransformReadField(Transformer next, CtField field,
+                             String methodClassname, String methodName)
+    {
+       super(next);
+       this.fieldClass = field.getDeclaringClass();
+       this.fieldname = field.getName();
+       this.methodClassname = methodClassname;
+       this.methodName = methodName;
+       this.isPrivate = Modifier.isPrivate(field.getModifiers());
+    }
+
+    static String isField(ClassPool pool, ConstPool cp, CtClass fclass,
+                         String fname, boolean is_private, int index) {
+       if (!cp.getFieldrefName(index).equals(fname))
+           return null;
+
+       try {
+           CtClass c = pool.get(cp.getFieldrefClassName(index));
+           if (is_private ? c == fclass : c.subclassOf(fclass))
+               return cp.getFieldrefType(index);
+       }
+       catch (NotFoundException e) {}
+       return null;
+    }
+
+    public int transform(CtClass tclazz, int pos, CodeIterator iterator,
+                        ConstPool cp) throws BadBytecode
+    {
+       int c = iterator.byteAt(pos);
+       if (c == GETFIELD || c == GETSTATIC) {
+           int index = iterator.u16bitAt(pos + 1);
+           String typedesc = isField(tclazz.getClassPool(), cp,
+                               fieldClass, fieldname, isPrivate, index);
+           if (typedesc != null) {
+               if (c == GETSTATIC) {
+                   iterator.move(pos);
+                   iterator.insertGap(1); // insertGap() may insert 4 bytes.
+                   iterator.writeByte(ACONST_NULL, pos);
+                   pos = iterator.next();
+               }
+
+               String type = "(Ljava/lang/Object;)" + typedesc;
+               int mi = cp.addClassInfo(methodClassname);
+               int methodref = cp.addMethodrefInfo(mi, methodName, type);
+               iterator.writeByte(INVOKESTATIC, pos);
+               iterator.write16bit(methodref, pos + 1);
+               return pos;
+           }
+       }
+
+       return pos;
+    }
+}
diff --git a/src/main/javassist/convert/TransformWriteField.java b/src/main/javassist/convert/TransformWriteField.java
new file mode 100644 (file)
index 0000000..8c37c7f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.convert;
+
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.bytecode.*;
+import javassist.CannotCompileException;
+
+final public class TransformWriteField extends TransformReadField {
+    public TransformWriteField(Transformer next, CtField field,
+                              String methodClassname, String methodName)
+    {
+       super(next, field, methodClassname, methodName);
+    }
+
+    public int transform(CtClass tclazz, int pos, CodeIterator iterator,
+                        ConstPool cp) throws BadBytecode
+    {
+       int c = iterator.byteAt(pos);
+       if (c == PUTFIELD || c == PUTSTATIC) {
+           int index = iterator.u16bitAt(pos + 1);
+           String typedesc = isField(tclazz.getClassPool(), cp,
+                               fieldClass, fieldname, isPrivate, index);
+           if (typedesc != null) {
+               if (c == PUTSTATIC) {
+                   CodeAttribute ca = iterator.get();
+                   iterator.move(pos);
+                   char c0 = typedesc.charAt(0);
+                   if (c0 == 'J' || c0 == 'D') {       // long or double
+                       // insertGap() may insert 4 bytes.
+                       iterator.insertGap(3);
+                       iterator.writeByte(ACONST_NULL, pos);
+                       iterator.writeByte(DUP_X2, pos + 1);
+                       iterator.writeByte(POP, pos + 2);
+                       ca.setMaxStack(ca.getMaxStack() + 2);
+                   }
+                   else {
+                       // insertGap() may insert 4 bytes.
+                       iterator.insertGap(2);
+                       iterator.writeByte(ACONST_NULL, pos);
+                       iterator.writeByte(SWAP, pos + 1);
+                       ca.setMaxStack(ca.getMaxStack() + 1);
+                   }
+
+                   pos = iterator.next();
+               }
+
+               int mi = cp.addClassInfo(methodClassname);
+               String type = "(Ljava/lang/Object;" + typedesc + ")V";
+               int methodref = cp.addMethodrefInfo(mi, methodName, type);
+               iterator.writeByte(INVOKESTATIC, pos);
+               iterator.write16bit(methodref, pos + 1);
+           }
+       }
+
+       return pos;
+    }
+}
diff --git a/src/main/javassist/convert/Transformer.java b/src/main/javassist/convert/Transformer.java
new file mode 100644 (file)
index 0000000..f1a4b56
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.convert;
+
+import javassist.bytecode.*;
+import javassist.CtClass;
+import javassist.CannotCompileException;
+
+/**
+ * Transformer and its subclasses are used for executing
+ * code transformation specified by CodeConverter.
+ *
+ * @see javassist.CodeConverter
+ */
+public abstract class Transformer implements Opcode {
+    private Transformer next;
+
+    public Transformer(Transformer t) {
+       next = t;
+    }
+
+    public Transformer getNext() { return next; }
+
+    public void initialize(ConstPool cp, CodeAttribute attr) {}
+
+    public void clean() {}
+
+    public abstract int transform(CtClass clazz, int pos, CodeIterator it,
+               ConstPool cp) throws CannotCompileException, BadBytecode;
+
+    public int extraLocals() { return 0; }
+}
diff --git a/src/main/javassist/expr/Cast.java b/src/main/javassist/expr/Cast.java
new file mode 100644 (file)
index 0000000..44648e7
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.expr;
+
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.*;
+import javassist.compiler.ast.ASTList;
+
+/**
+ * Explicit type cast.
+ */
+public class Cast extends Expr {
+    /**
+     * Undocumented constructor.  Do not use; internal-use only.
+     */
+    Cast(int pos, CodeIterator i, CtClass declaring, MethodInfo m) {
+       super(pos, i, declaring, m);
+    }
+
+    /**
+     * Returns the method or constructor containing the type cast
+     * expression represented by this object.
+     */
+    public CtBehavior where() { return super.where(); }
+
+    /**
+     * Returns the line number of the source line containing the
+     * type-cast expression.
+     *
+     * @return -1      if this information is not available.
+     */
+    public int getLineNumber() {
+       return super.getLineNumber();
+    }
+
+    /**
+     * Returns the source file containing the type-cast expression.
+     *
+     * @return null    if this information is not available.
+     */
+    public String getFileName() {
+       return super.getFileName();
+    }
+
+    /**
+     * Returns the <code>CtClass</code> object representing
+     * the type specified by the cast.
+     */
+    public CtClass getType() throws NotFoundException {
+       ConstPool cp = getConstPool();
+       int pos = currentPos;
+       int index = iterator.u16bitAt(pos + 1);
+       String name = cp.getClassInfo(index);
+       return Descriptor.toCtClass(name, thisClass.getClassPool());
+    }
+
+    /**
+     * Returns the list of exceptions that the expression may throw.
+     * This list includes both the exceptions that the try-catch statements
+     * including the expression can catch and the exceptions that
+     * the throws declaration allows the method to throw.
+     */
+    public CtClass[] mayThrow() {
+       return super.mayThrow();
+    }
+
+    /**
+     * Replaces the explicit cast operator with the bytecode derived from
+     * the given source text.
+     *
+     * <p>$0 is available but the value is <code>null</code>.
+     *
+     * @param statement                a Java statement.
+     */
+    public void replace(String statement) throws CannotCompileException {
+       ConstPool constPool = getConstPool();
+       int pos = currentPos;
+       int index = iterator.u16bitAt(pos + 1);
+
+       Javac jc = new Javac(thisClass);
+       ClassPool cp = thisClass.getClassPool();
+       CodeAttribute ca = iterator.get();
+
+       try {
+           CtClass[] params
+               = new CtClass[] { cp.get(javaLangObject) };
+           CtClass retType = getType();
+
+           int paramVar = ca.getMaxLocals();
+           jc.recordParams(javaLangObject, params, true, paramVar,
+                           withinStatic());
+           int retVar = jc.recordReturnType(retType, true);
+           jc.recordProceed(new ProceedForCast(index, retType));
+
+           /* Is $_ included in the source code?
+            */
+           checkResultValue(retType, statement);
+
+           Bytecode bytecode = jc.getBytecode();
+           storeStack(params, true, paramVar, bytecode);
+           jc.compileStmnt(statement);
+           bytecode.addLoad(retVar, retType);
+
+           replace0(pos, bytecode, 3);
+       }
+       catch (CompileError e) { throw new CannotCompileException(e); }
+       catch (NotFoundException e) { throw new CannotCompileException(e); }
+       catch (BadBytecode e) {
+           throw new CannotCompileException("broken method");
+       }
+    }
+
+    /* <type> $proceed(Object obj)
+     */
+    static class ProceedForCast implements ProceedHandler {
+       int index;
+       CtClass retType;
+
+       ProceedForCast(int i, CtClass t) {
+           index = i;
+           retType = t;
+       }
+
+       public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
+           throws CompileError
+       {
+           if (gen.atMethodArgsLength(args) != 1)
+               throw new CompileError(Javac.proceedName
+                       + "() cannot take more than one parameter "
+                       + "for cast");
+
+           gen.atMethodArgs(args, new int[1], new int[1], new String[1]);
+           bytecode.addOpcode(Opcode.CHECKCAST);
+           bytecode.addIndex(index);
+           gen.setType(retType);
+       }
+    }
+}
diff --git a/src/main/javassist/expr/Expr.java b/src/main/javassist/expr/Expr.java
new file mode 100644 (file)
index 0000000..d88d793
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.expr;
+
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.*;
+import java.util.LinkedList;
+import java.util.Iterator;
+
+/**
+ * Caller-side expression.
+ */
+abstract class Expr implements Opcode {
+    int currentPos;
+    CodeIterator iterator;
+    CtClass thisClass;
+    MethodInfo thisMethod;
+
+    boolean edited;
+    int maxLocals, maxStack;
+
+    static final String javaLangObject = "java.lang.Object";
+
+    Expr(int pos, CodeIterator i, CtClass declaring, MethodInfo m) {
+       currentPos = pos;
+       iterator = i;
+       thisClass = declaring;
+       thisMethod = m;
+    }
+
+    final ConstPool getConstPool() {
+       return thisMethod.getConstPool();
+    }
+
+    final boolean edited() { return edited; }
+
+    final int locals() { return maxLocals; }
+
+    final int stack() { return maxStack; }
+
+    /**
+     * Returns true if this method is static.
+     */
+    final boolean withinStatic() {
+       return (thisMethod.getAccessFlags() & AccessFlag.STATIC) != 0;
+    }
+
+    /**
+     * Returns the constructor or method containing the expression.
+     */
+    public CtBehavior where() {
+       MethodInfo mi = thisMethod;
+       CtBehavior[] cb = thisClass.getDeclaredBehaviors();
+       for (int i = cb.length - 1; i >= 0; --i)
+           if (cb[i].getMethodInfo() == mi)
+               return cb[i];
+
+       throw new RuntimeException("fatal: not found");
+    }
+
+    /**
+     * Returns the list of exceptions that the expression may throw.
+     * This list includes both the exceptions that the try-catch statements
+     * including the expression can catch and the exceptions that
+     * the throws declaration allows the method to throw.
+     */
+    public CtClass[] mayThrow() {
+       ClassPool pool = thisClass.getClassPool();
+       ConstPool cp = thisMethod.getConstPool();
+       LinkedList list = new LinkedList();
+       try {
+           CodeAttribute ca = thisMethod.getCodeAttribute();
+           ExceptionTable et = ca.getExceptionTable();
+           int pos = currentPos;
+           int n = et.size();
+           for (int i = 0; i < n; ++i)
+               if (et.startPc(i) <= pos && pos < et.endPc(i)) {
+                   int t = et.catchType(i);
+                   if (t > 0)
+                       try {
+                           addClass(list, pool.get(cp.getClassInfo(t)));
+                       }
+                       catch (NotFoundException e) {}
+               }
+       }
+       catch (NullPointerException e) {}
+
+       ExceptionsAttribute ea = thisMethod.getExceptionsAttribute();
+       if (ea != null) {
+           String[] exceptions = ea.getExceptions();
+           if (exceptions != null) {
+               int n = exceptions.length;
+               for (int i = 0; i < n; ++i)
+                   try {
+                       addClass(list, pool.get(exceptions[i]));
+                   }
+                   catch (NotFoundException e) {}
+           }
+       }
+
+       return (CtClass[])list.toArray(new CtClass[list.size()]);
+    }
+
+    private static void addClass(LinkedList list, CtClass c) {
+       Iterator it = list.iterator();
+       while (it.hasNext())
+           if (it.next() == c)
+               return;
+
+       list.add(c);
+    }
+
+    /**
+     * Returns the line number of the source line containing the
+     * expression.
+     *
+     * @return -1      if this information is not available.
+     */
+    public int getLineNumber() {
+       return thisMethod.getLineNumber(currentPos);
+    }
+
+    /**
+     * Returns the source file containing the expression.
+     *
+     * @return null    if this information is not available.
+     */
+    public String getFileName() {
+       ClassFile cf = thisClass.getClassFile2();
+       if (cf == null)
+           return null;
+       else
+           return cf.getSourceFile();
+    }
+
+    static final boolean checkResultValue(CtClass retType, String prog)
+       throws CannotCompileException
+    {
+       /* Is $_ included in the source code?
+        */
+       boolean hasIt = (prog.indexOf(Javac.resultVarName) >= 0);
+       if (!hasIt && retType != CtClass.voidType)
+           throw new CannotCompileException(
+                       "the resulting value is not stored in "
+                       + Javac.resultVarName);
+
+       return hasIt;
+    }
+
+    /* If isStaticCall is true, null is assigned to $0.  So $0 must
+     * be declared by calling Javac.recordParams().
+     *
+     * After executing this method, the current stack depth might
+     * be less than 0.
+     */
+    static final void storeStack(CtClass[] params, boolean isStaticCall,
+                                int regno, Bytecode bytecode) {
+       storeStack0(0, params.length, params, regno + 1, bytecode);
+       if (isStaticCall)
+           bytecode.addOpcode(ACONST_NULL);
+
+       bytecode.addAstore(regno);
+    }
+
+    private static void storeStack0(int i, int n, CtClass[] params,
+                                   int regno, Bytecode bytecode) {
+       if (i >= n)
+           return;
+       else {
+           CtClass c = params[i];
+           int size;
+           if (c instanceof CtPrimitiveType)
+               size = ((CtPrimitiveType)c).getDataSize();
+           else
+               size = 1;
+
+           storeStack0(i + 1, n, params, regno + size, bytecode);
+           bytecode.addStore(regno, c);
+       }
+    }
+
+    protected void replace0(int pos, Bytecode bytecode, int size)
+       throws BadBytecode
+    {
+       byte[] code = bytecode.get();
+       edited = true;
+       int gap = code.length - size;
+       if (gap > 0)
+           iterator.insertGap(pos, gap);
+       else
+           for (int i = 0; i < size; ++i)
+               iterator.writeByte(NOP, pos + i);
+
+       iterator.write(code, pos);
+       iterator.insert(bytecode.getExceptionTable(), pos);
+       maxLocals = bytecode.getMaxLocals();
+       maxStack = bytecode.getMaxStack();
+    }
+}
diff --git a/src/main/javassist/expr/ExprEditor.java b/src/main/javassist/expr/ExprEditor.java
new file mode 100644 (file)
index 0000000..c6814ef
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.expr;
+
+import javassist.bytecode.*;
+import javassist.CtClass;
+import javassist.CannotCompileException;
+
+/**
+ * A translator of method bodies.
+ *
+ * <p>The users can define a subclass of this class to customize how to
+ * modify a method body.  The overall architecture is similar to the
+ * strategy pattern.
+ *
+ * <p>If <code>instrument()</code> is called in
+ * <code>CtMethod</code>, the method body is scanned from the beginning
+ * to the end.
+ * Whenever an expression, such as a method call and a <tt>new</tt>
+ * expression (object creation),
+ * is found, <code>edit()</code> is called in <code>ExprEdit</code>.
+ * <code>edit()</code> can inspect and modify the given expression.
+ * The modification is reflected on the original method body.  If
+ * <code>edit()</code> does nothing, the original method body is not
+ * changed.
+ *
+ * <p>The following code is an example:
+ *
+ * <ul><pre>
+ * CtMethod cm = ...;
+ * cm.instrument(new ExprEditor() {
+ *     public void edit(MethodCall m) throws CannotCompileException {
+ *         if (m.getClassName().equals("Point")) {
+ *             System.out.println(m.getMethodName() + " line: "
+ *                                + m.getLineNumber());
+ *     }
+ * });
+ * </pre></ul>
+ *
+ * <p>This code inspects all method calls appearing in the method represented
+ * by <code>cm</code> and it prints the names and the line numbers of the
+ * methods declared in class <code>Point</code>.  This code does not modify
+ * the body of the method represented by <code>cm</code>.  If the method
+ * body must be modified, call <code>replace()</code>
+ * in <code>MethodCall</code>.
+ *
+ * @see javassist.CtClass#instrument(ExprEditor)
+ * @see javassist.CtMethod#instrument(ExprEditor)
+ * @see javassist.CtConstructor#instrument(ExprEditor)
+ * @see MethodCall
+ * @see NewExpr
+ * @see FieldAccess
+ *
+ * @see javassist.CodeConverter
+ */
+public class ExprEditor {
+    /**
+     * Default constructor.  It does nothing.
+     */
+    public ExprEditor() {}
+
+    static class NewOp {
+       NewOp next;
+       int pos;
+       String type;
+
+       NewOp(NewOp n, int p, String t) {
+           next = n;
+           pos = p;
+           type = t;
+       }
+    }
+
+    /**
+     * Undocumented method.  Do not use; internal-use only.
+     */
+    public boolean doit(CtClass clazz, MethodInfo minfo)
+       throws CannotCompileException
+    {
+       CodeAttribute codeAttr = minfo.getCodeAttribute();
+       if (codeAttr == null)
+           return false;
+
+       CodeIterator iterator = codeAttr.iterator();
+       boolean edited = false;
+       int maxLocals = codeAttr.getMaxLocals();
+       int maxStack = 0;
+
+       NewOp newList = null;
+       ConstPool cp = minfo.getConstPool();
+
+       while (iterator.hasNext())
+           try {
+               Expr expr = null;
+               int pos = iterator.next();
+               int c = iterator.byteAt(pos);
+
+               if (c == Opcode.INVOKESTATIC || c == Opcode.INVOKEINTERFACE
+                   || c == Opcode.INVOKEVIRTUAL) {
+                   expr = new MethodCall(pos, iterator, clazz, minfo);
+                   edit((MethodCall)expr);
+               }
+               else if (c == Opcode.GETFIELD || c == Opcode.GETSTATIC
+                       || c == Opcode.PUTFIELD || c == Opcode.PUTSTATIC) {
+                   expr = new FieldAccess(pos, iterator, clazz, minfo, c);
+                   edit((FieldAccess)expr);
+               }
+               else if (c == Opcode.NEW) {
+                   int index = iterator.u16bitAt(pos + 1);
+                   newList = new NewOp(newList, pos,
+                                       cp.getClassInfo(index));
+               }
+               else if (c == Opcode.INVOKESPECIAL) {
+                   if (newList != null && cp.isConstructor(newList.type,
+                               iterator.u16bitAt(pos + 1)) > 0) {
+                       expr = new NewExpr(pos, iterator, clazz, minfo,
+                                          newList.type, newList.pos);
+                       edit((NewExpr)expr);
+                       newList = newList.next;
+                   }
+                   else {
+                       expr = new MethodCall(pos, iterator, clazz, minfo);
+                       MethodCall mcall = (MethodCall)expr;
+                       if (!mcall.getMethodName().equals(
+                                               MethodInfo.nameInit))
+                           edit(mcall);
+                   }
+               }
+               else if (c == Opcode.INSTANCEOF) {
+                   expr = new Instanceof(pos, iterator, clazz, minfo);
+                   edit((Instanceof)expr);
+               }
+               else if (c == Opcode.CHECKCAST) {
+                   expr = new Cast(pos, iterator, clazz, minfo);
+                   edit((Cast)expr);
+               }
+
+               if (expr != null && expr.edited()) {
+                   edited = true;
+                   if (maxLocals < expr.locals())
+                       maxLocals = expr.locals();
+
+                   if (maxStack < expr.stack())
+                       maxStack = expr.stack();
+               }
+           }
+           catch (BadBytecode e) {
+               throw new CannotCompileException(e);
+           }
+
+       codeAttr.setMaxLocals(maxLocals);
+       codeAttr.setMaxStack(codeAttr.getMaxStack() + maxStack);
+       return edited;
+    }
+
+    /**
+     * Edits a <tt>new</tt> expression (overridable).
+     * The default implementation performs nothing.
+     *
+     * @param e                the <tt>new</tt> expression creating an object.
+     */
+    public void edit(NewExpr e) throws CannotCompileException {}
+
+    /**
+     * Edits a method call (overridable).
+     * The default implementation performs nothing.
+     */
+    public void edit(MethodCall m) throws CannotCompileException {}
+
+    /**
+     * Edits a field-access expression (overridable).
+     * Field access means both read and write.
+     * The default implementation performs nothing.
+     */
+    public void edit(FieldAccess f) throws CannotCompileException {}
+
+    /**
+     * Edits an instanceof expression (overridable).
+     * The default implementation performs nothing.
+     */
+    public void edit(Instanceof i) throws CannotCompileException {}
+
+    /**
+     * Edits an expression for explicit type casting (overridable).
+     * The default implementation performs nothing.
+     */
+    public void edit(Cast c) throws CannotCompileException {}
+}
diff --git a/src/main/javassist/expr/FieldAccess.java b/src/main/javassist/expr/FieldAccess.java
new file mode 100644 (file)
index 0000000..a524b07
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.expr;
+
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.*;
+import javassist.compiler.ast.ASTList;
+
+/**
+ * Expression for accessing a field.
+ */
+public class FieldAccess extends Expr {
+    int opcode;
+
+    FieldAccess(int pos, CodeIterator i, CtClass declaring, MethodInfo m,
+               int op) {
+       super(pos, i, declaring, m);
+       opcode = op;
+    }
+
+    /**
+     * Returns the method or constructor containing the field-access
+     * expression represented by this object.
+     */
+    public CtBehavior where() { return super.where(); }
+
+    /**
+     * Returns the line number of the source line containing the
+     * field access.
+     *
+     * @return -1      if this information is not available.
+     */
+    public int getLineNumber() {
+       return super.getLineNumber();
+    }
+
+    /**
+     * Returns the source file containing the field access.
+     *
+     * @return null    if this information is not available.
+     */
+    public String getFileName() {
+       return super.getFileName();
+    }
+
+    /**
+     * Returns true if the field is static.
+     */
+    public boolean isStatic() {
+       return isStatic(opcode);
+    }
+
+    static boolean isStatic(int c) {
+       return c == Opcode.GETSTATIC || c == Opcode.PUTSTATIC;
+    }
+
+    /**
+     * Returns true if the field is read.
+     */
+    public boolean isReader() {
+       return opcode == Opcode.GETFIELD || opcode ==  Opcode.GETSTATIC;
+    }
+
+    /**
+     * Returns true if the field is written in.
+     */
+    public boolean isWriter() {
+       return opcode == Opcode.PUTFIELD || opcode ==  Opcode.PUTSTATIC;
+    }
+
+    /**
+     * Returns the class in which the field is declared.
+     */
+    private CtClass getCtClass() throws NotFoundException {
+       return thisClass.getClassPool().get(getClassName());
+    }
+
+    /**
+     * Returns the name of the class in which the field is declared.
+     */
+    public String getClassName() {
+       int index = iterator.u16bitAt(currentPos + 1);
+       return getConstPool().getFieldrefClassName(index);
+    }
+
+    /**
+     * Returns the name of the field.
+     */
+    public String getFieldName() {
+       int index = iterator.u16bitAt(currentPos + 1);
+       return getConstPool().getFieldrefName(index);
+    }
+
+    /**
+     * Returns the field accessed by this expression.
+     */
+    public CtField getField() throws NotFoundException {
+       CtClass cc = getCtClass();
+       return cc.getField(getFieldName());
+    }
+
+    /**
+     * Returns the list of exceptions that the expression may throw.
+     * This list includes both the exceptions that the try-catch statements
+     * including the expression can catch and the exceptions that
+     * the throws declaration allows the method to throw.
+     */
+    public CtClass[] mayThrow() {
+       return super.mayThrow();
+    }
+
+    /*
+     * Returns the type of the field.
+
+    public CtClass getFieldType() throws NotFoundException {
+       int index = iterator.u16bitAt(currentPos + 1);
+       String type = getConstPool().getFieldrefType(index);
+       return Descriptor.toCtClass(type, thisClass.getClassPool());
+    }
+    */
+
+    /**
+     * Replaces the method call with the bytecode derived from
+     * the given source text.
+     *
+     * <p>$0 is available even if the called method is static.
+     * If the field access is writing, $_ is available but the value
+     * of $_ is ignored.
+     *
+     * @param statement                a Java statement.
+     */
+    public void replace(String statement) throws CannotCompileException {
+       ConstPool constPool = getConstPool();
+       int pos = currentPos;
+       int index = iterator.u16bitAt(pos + 1);
+
+       Javac jc = new Javac(thisClass);
+       CodeAttribute ca = iterator.get();
+       try {
+           CtClass[] params;
+           CtClass retType;
+           CtClass fieldType
+               = Descriptor.toCtClass(constPool.getFieldrefType(index),
+                                      thisClass.getClassPool());
+           boolean read = isReader();
+           if (read) {
+               params = new CtClass[0];
+               retType = fieldType;
+           }
+           else {
+               params = new CtClass[1];
+               params[0] = fieldType;
+               retType = CtClass.voidType;
+           }
+
+           int paramVar = ca.getMaxLocals();
+           jc.recordParams(constPool.getFieldrefClassName(index), params,
+                           true, paramVar, withinStatic());
+
+           /* Is $_ included in the source code?
+            */
+           boolean included = checkResultValue(retType, statement);
+
+           int retVar = jc.recordReturnType(retType, included);
+           if (read)
+               jc.recordProceed(new ProceedForRead(retType, opcode,
+                                                   index, paramVar));
+           else {
+               // because $type is not the return type...
+               jc.recordType(fieldType);
+               jc.recordProceed(new ProceedForWrite(params[0], opcode,
+                                                    index, paramVar));
+           }
+
+           Bytecode bytecode = jc.getBytecode();
+           storeStack(params, isStatic(), paramVar, bytecode);
+           jc.compileStmnt(statement);
+           if (read)
+               bytecode.addLoad(retVar, retType);
+
+           replace0(pos, bytecode, 3);
+       }
+       catch (CompileError e) { throw new CannotCompileException(e); }
+       catch (NotFoundException e) { throw new CannotCompileException(e); }
+       catch (BadBytecode e) {
+           throw new CannotCompileException("broken method");
+       }
+    }
+
+    /* <field type> $proceed()
+     */
+    static class ProceedForRead implements ProceedHandler {
+       CtClass fieldType;
+       int opcode;
+       int targetVar, index;
+
+       ProceedForRead(CtClass type, int op, int i, int var) {
+           fieldType = type;
+           targetVar = var;
+           opcode = op;
+           index = i;
+       }
+
+       public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
+           throws CompileError
+       {
+           if (args != null && !gen.isParamListName(args))
+               throw new CompileError(Javac.proceedName
+                       + "() cannot take a parameter for field reading");
+
+           int stack;
+           if (isStatic(opcode))
+               stack = 0;
+           else {
+               stack = -1;
+               bytecode.addAload(targetVar);
+           }
+
+           if (fieldType instanceof CtPrimitiveType)
+               stack += ((CtPrimitiveType)fieldType).getDataSize();
+           else
+               ++stack;
+
+           bytecode.add(opcode);
+           bytecode.addIndex(index);
+           bytecode.growStack(stack);
+           gen.setType(fieldType);
+       }
+    }
+
+    /* void $proceed(<field type>)
+     *         the return type is not the field type but void.
+     */
+    static class ProceedForWrite implements ProceedHandler {
+       CtClass fieldType;
+       int opcode;
+       int targetVar, index;
+
+       ProceedForWrite(CtClass type, int op, int i, int var) {
+           fieldType = type;
+           targetVar = var;
+           opcode = op;
+           index = i;
+       }
+
+       public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
+           throws CompileError
+       {
+           if (gen.atMethodArgsLength(args) != 1)
+               throw new CompileError(Javac.proceedName
+                       + "() cannot take more than one parameter "
+                       + "for field writing");
+
+           int stack;
+           if (isStatic(opcode))
+               stack = 0;
+           else {
+               stack = -1;
+               bytecode.addAload(targetVar);
+           }
+
+           gen.atMethodArgs(args, new int[1], new int[1], new String[1]);
+           gen.doNumCast(fieldType);
+           if (fieldType instanceof CtPrimitiveType)
+               stack -= ((CtPrimitiveType)fieldType).getDataSize();
+           else
+               --stack;
+
+           bytecode.add(opcode);
+           bytecode.addIndex(index);
+           bytecode.growStack(stack);
+           gen.setType(CtClass.voidType);
+           gen.addNullIfVoid();
+       }
+    }
+}
diff --git a/src/main/javassist/expr/Instanceof.java b/src/main/javassist/expr/Instanceof.java
new file mode 100644 (file)
index 0000000..5b980f8
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.expr;
+
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.*;
+import javassist.compiler.ast.ASTList;
+
+/**
+ * Instanceof operator.
+ */
+public class Instanceof extends Expr {
+    /**
+     * Undocumented constructor.  Do not use; internal-use only.
+     */
+    Instanceof(int pos, CodeIterator i, CtClass declaring, MethodInfo m) {
+       super(pos, i, declaring, m);
+    }
+
+    /**
+     * Returns the method or constructor containing the instanceof
+     * expression represented by this object.
+     */
+    public CtBehavior where() { return super.where(); }
+
+    /**
+     * Returns the line number of the source line containing the
+     * instanceof expression.
+     *
+     * @return -1      if this information is not available.
+     */
+    public int getLineNumber() {
+       return super.getLineNumber();
+    }
+
+    /**
+     * Returns the source file containing the
+     * instanceof expression.
+     *
+     * @return null    if this information is not available.
+     */
+    public String getFileName() {
+       return super.getFileName();
+    }
+
+    /**
+     * Returns the <code>CtClass</code> object representing
+     * the type name on the right hand side
+     * of the instanceof operator.
+     */
+    public CtClass getType() throws NotFoundException {
+       ConstPool cp = getConstPool();
+       int pos = currentPos;
+       int index = iterator.u16bitAt(pos + 1);
+       String name = cp.getClassInfo(index);
+       return Descriptor.toCtClass(name, thisClass.getClassPool());
+    }
+
+    /**
+     * Returns the list of exceptions that the expression may throw.
+     * This list includes both the exceptions that the try-catch statements
+     * including the expression can catch and the exceptions that
+     * the throws declaration allows the method to throw.
+     */
+    public CtClass[] mayThrow() {
+       return super.mayThrow();
+    }
+
+    /**
+     * Replaces the instanceof operator with the bytecode derived from
+     * the given source text.
+     *
+     * <p>$0 is available but the value is <code>null</code>.
+     *
+     * @param statement                a Java statement.
+     */
+    public void replace(String statement) throws CannotCompileException {
+       ConstPool constPool = getConstPool();
+       int pos = currentPos;
+       int index = iterator.u16bitAt(pos + 1);
+
+       Javac jc = new Javac(thisClass);
+       ClassPool cp = thisClass.getClassPool();
+       CodeAttribute ca = iterator.get();
+
+       try {
+           CtClass[] params
+               = new CtClass[] { cp.get(javaLangObject) };
+           CtClass retType = CtClass.booleanType;
+
+           int paramVar = ca.getMaxLocals();
+           jc.recordParams(javaLangObject, params, true, paramVar,
+                           withinStatic());
+           int retVar = jc.recordReturnType(retType, true);
+           jc.recordProceed(new ProceedForInstanceof(index));
+
+           // because $type is not the return type...
+           jc.recordType(getType());
+
+           /* Is $_ included in the source code?
+            */
+           checkResultValue(retType, statement);
+
+           Bytecode bytecode = jc.getBytecode();
+           storeStack(params, true, paramVar, bytecode);
+           jc.compileStmnt(statement);
+           bytecode.addLoad(retVar, retType);
+
+           replace0(pos, bytecode, 3);
+       }
+       catch (CompileError e) { throw new CannotCompileException(e); }
+       catch (NotFoundException e) { throw new CannotCompileException(e); }
+       catch (BadBytecode e) {
+           throw new CannotCompileException("broken method");
+       }
+    }
+
+    /* boolean $proceed(Object obj)
+     */
+    static class ProceedForInstanceof implements ProceedHandler {
+       int index;
+
+       ProceedForInstanceof(int i) {
+           index = i;
+       }
+
+       public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
+           throws CompileError
+       {
+           if (gen.atMethodArgsLength(args) != 1)
+               throw new CompileError(Javac.proceedName
+                       + "() cannot take more than one parameter "
+                       + "for instanceof");
+
+           gen.atMethodArgs(args, new int[1], new int[1], new String[1]);
+           bytecode.addOpcode(Opcode.INSTANCEOF);
+           bytecode.addIndex(index);
+           gen.setType(CtClass.booleanType);
+       }
+    }
+}
diff --git a/src/main/javassist/expr/MethodCall.java b/src/main/javassist/expr/MethodCall.java
new file mode 100644 (file)
index 0000000..747380b
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.expr;
+
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.*;
+
+/**
+ * Method invocation (caller-side expression).
+ */
+public class MethodCall extends Expr {
+    /**
+     * Undocumented constructor.  Do not use; internal-use only.
+     */
+    MethodCall(int pos, CodeIterator i, CtClass declaring, MethodInfo m) {
+       super(pos, i, declaring, m);
+    }
+
+    private int getNameAndType(ConstPool cp) {
+       String cname;
+       int pos = currentPos;
+       int c = iterator.byteAt(pos);
+       int index = iterator.u16bitAt(pos + 1);
+
+       if (c == INVOKEINTERFACE)
+           return cp.getInterfaceMethodrefNameAndType(index);
+       else
+           return cp.getMethodrefNameAndType(index);
+    }
+
+    /**
+     * Returns the method or constructor containing the method-call
+     * expression represented by this object.
+     */
+    public CtBehavior where() { return super.where(); }
+
+    /**
+     * Returns the line number of the source line containing the
+     * method call.
+     *
+     * @return -1      if this information is not available.
+     */
+    public int getLineNumber() {
+       return super.getLineNumber();
+    }
+
+    /**
+     * Returns the source file containing the method call.
+     *
+     * @return null    if this information is not available.
+     */
+    public String getFileName() {
+       return super.getFileName();
+    }
+
+    /**
+     * Returns the class of the target object,
+     * which the method is called on.
+     */
+    private CtClass getCtClass() throws NotFoundException {
+       return thisClass.getClassPool().get(getClassName());
+    }
+
+    /**
+     * Returns the class name of the target object,
+     * which the method is called on.
+     */
+    public String getClassName() {
+       String cname;
+
+       ConstPool cp = getConstPool();
+       int pos = currentPos;
+       int c = iterator.byteAt(pos);
+       int index = iterator.u16bitAt(pos + 1);
+
+       if (c == INVOKEINTERFACE)
+           cname = cp.getInterfaceMethodrefClassName(index);
+       else
+           cname = cp.getMethodrefClassName(index);
+
+       return cname;
+    }
+
+    /**
+     * Returns the name of the called method.
+     */
+    public String getMethodName() {
+       ConstPool cp = getConstPool();
+       int nt = getNameAndType(cp);
+       return cp.getUtf8Info(cp.getNameAndTypeName(nt));
+    }
+
+    /**
+     * Returns the called method.
+     */
+    public CtMethod getMethod() throws NotFoundException {
+       return getCtClass().getMethod(getMethodName(), getMethodDesc());
+    }
+
+    private String getMethodDesc() {
+       ConstPool cp = getConstPool();
+       int nt = getNameAndType(cp);
+       return cp.getUtf8Info(cp.getNameAndTypeDescriptor(nt));
+    }
+
+    /**
+     * Returns the list of exceptions that the expression may throw.
+     * This list includes both the exceptions that the try-catch statements
+     * including the expression can catch and the exceptions that
+     * the throws declaration allows the method to throw.
+     */
+    public CtClass[] mayThrow() {
+       return super.mayThrow();
+    }
+
+    /*
+     * Returns the parameter types of the called method.
+
+    public CtClass[] getParameterTypes() throws NotFoundException {
+       return Descriptor.getParameterTypes(getMethodDesc(),
+                                           thisClass.getClassPool());
+    }
+    */
+
+    /*
+     * Returns the return type of the called method.
+
+    public CtClass getReturnType() throws NotFoundException {
+       return Descriptor.getReturnType(getMethodDesc(),
+                                       thisClass.getClassPool());
+    }
+    */
+
+    /**
+     * Replaces the method call with the bytecode derived from
+     * the given source text.
+     *
+     * <p>$0 is available even if the called method is static.
+     *
+     * @param statement                a Java statement.
+     */
+    public void replace(String statement) throws CannotCompileException {
+       ConstPool constPool = getConstPool();
+       int pos = currentPos;
+       int index = iterator.u16bitAt(pos + 1);
+
+       String classname, methodname, signature;
+       int opcodeSize;
+       int c = iterator.byteAt(pos);
+       if (c == INVOKEINTERFACE) {
+           opcodeSize = 5;
+           classname = constPool.getInterfaceMethodrefClassName(index);
+           methodname = constPool.getInterfaceMethodrefName(index);
+           signature = constPool.getInterfaceMethodrefType(index);
+       }
+       else if (c == INVOKESTATIC
+                || c == INVOKESPECIAL || c == INVOKEVIRTUAL) {
+           opcodeSize = 3;
+           classname = constPool.getMethodrefClassName(index);
+           methodname = constPool.getMethodrefName(index);
+           signature = constPool.getMethodrefType(index);
+       }
+       else
+           throw new CannotCompileException("not method invocation");
+
+       Javac jc = new Javac(thisClass);
+       ClassPool cp = thisClass.getClassPool();
+       CodeAttribute ca = iterator.get();
+       try {
+           CtClass[] params = Descriptor.getParameterTypes(signature, cp);
+           CtClass retType = Descriptor.getReturnType(signature, cp);
+           int paramVar = ca.getMaxLocals();
+           jc.recordParams(classname, params,
+                           true, paramVar, withinStatic());
+           int retVar = jc.recordReturnType(retType, true);
+           jc.recordProceed(Javac.param0Name, methodname);
+
+           /* Is $_ included in the source code?
+            */
+           checkResultValue(retType, statement);
+
+           Bytecode bytecode = jc.getBytecode();
+           storeStack(params, c == INVOKESTATIC, paramVar, bytecode);
+           jc.compileStmnt(statement);
+           if (retType != CtClass.voidType)
+               bytecode.addLoad(retVar, retType);
+
+           replace0(pos, bytecode, opcodeSize);
+       }
+       catch (CompileError e) { throw new CannotCompileException(e); }
+       catch (NotFoundException e) { throw new CannotCompileException(e); }
+       catch (BadBytecode e) {
+           throw new CannotCompileException("broken method");
+       }
+    }
+}
diff --git a/src/main/javassist/expr/NewExpr.java b/src/main/javassist/expr/NewExpr.java
new file mode 100644 (file)
index 0000000..7354623
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.expr;
+
+import javassist.*;
+import javassist.bytecode.*;
+import javassist.compiler.*;
+import javassist.compiler.ast.ASTree;
+import javassist.compiler.ast.ASTList;
+
+/**
+ * Object creation (<tt>new</tt> expression).
+ */
+public class NewExpr extends Expr {
+    String newTypeName;
+    int newPos;
+
+    /**
+     * Undocumented constructor.  Do not use; internal-use only.
+     */
+    NewExpr(int pos, CodeIterator i, CtClass declaring, MethodInfo m,
+           String type, int np)
+    {
+       super(pos, i, declaring, m);
+       newTypeName = type;
+       newPos = np;
+    }
+
+    private int getNameAndType(ConstPool cp) {
+       String cname;
+       int pos = currentPos;
+       int c = iterator.byteAt(pos);
+       int index = iterator.u16bitAt(pos + 1);
+
+       if (c == INVOKEINTERFACE)
+           return cp.getInterfaceMethodrefNameAndType(index);
+       else
+           return cp.getMethodrefNameAndType(index);
+    }
+
+    /**
+     * Returns the method or constructor containing the <tt>new</tt>
+     * expression represented by this object.
+     */
+    public CtBehavior where() { return super.where(); }
+
+    /**
+     * Returns the line number of the source line containing the
+     * <tt>new</tt> expression.
+     *
+     * @return -1      if this information is not available.
+     */
+    public int getLineNumber() {
+       return super.getLineNumber();
+    }
+
+    /**
+     * Returns the source file containing the <tt>new</tt> expression.
+     *
+     * @return null    if this information is not available.
+     */
+    public String getFileName() {
+       return super.getFileName();
+    }
+
+    /**
+     * Returns the class of the created object.
+     */
+    private CtClass getCtClass() throws NotFoundException {
+       return thisClass.getClassPool().get(newTypeName);
+    }
+
+    /**
+     * Returns the class name of the created object.
+     */
+    public String getClassName() {
+       return newTypeName;
+    }
+
+    /**
+     * Returns the constructor called for creating the object.
+     */
+    public CtConstructor getConstructor() throws NotFoundException {
+       ConstPool cp = getConstPool();
+       int index = iterator.u16bitAt(currentPos + 1);
+       String desc = cp.getMethodrefType(index);
+       return getCtClass().getConstructor(desc);
+    }
+
+    /**
+     * Returns the list of exceptions that the expression may throw.
+     * This list includes both the exceptions that the try-catch statements
+     * including the expression can catch and the exceptions that
+     * the throws declaration allows the method to throw.
+     */
+    public CtClass[] mayThrow() {
+       return super.mayThrow();
+    }
+
+    /*
+     * Returns the parameter types of the constructor.
+
+    public CtClass[] getParameterTypes() throws NotFoundException {
+       ConstPool cp = getConstPool();
+       int index = iterator.u16bitAt(currentPos + 1);
+       String desc = cp.getMethodrefType(index);
+       return Descriptor.getParameterTypes(desc, thisClass.getClassPool());
+    }
+    */
+
+    private int canReplace() throws CannotCompileException {
+       int op = iterator.byteAt(newPos + 3);
+       if (op == Opcode.DUP)
+           return 4;
+       else if (op == Opcode.DUP_X1
+                && iterator.byteAt(newPos + 4) == Opcode.SWAP)
+           return 5;
+       else
+           throw new CannotCompileException(
+                       "sorry, cannot edit NEW followed by no DUP");
+    }
+
+    /**
+     * Replaces the <tt>new</tt> expression with the bytecode derived from
+     * the given source text.
+     *
+     * <p>$0 is available but the value is null.
+     *
+     * @param statement                a Java statement.
+     */
+    public void replace(String statement) throws CannotCompileException {
+       final int bytecodeSize = 3;
+       int pos = newPos;
+
+       int newIndex = iterator.u16bitAt(pos + 1);
+
+       /* delete the preceding NEW and DUP (or DUP_X1, SWAP) instructions.
+        */
+       int end = pos + canReplace();
+       for (int i = pos; i < end; ++i)
+           iterator.writeByte(NOP, i);
+
+       ConstPool constPool = getConstPool();
+       pos = currentPos;
+       int methodIndex = iterator.u16bitAt(pos + 1);   // constructor
+
+       String signature = constPool.getMethodrefType(methodIndex);
+
+       Javac jc = new Javac(thisClass);
+       ClassPool cp = thisClass.getClassPool();
+       CodeAttribute ca = iterator.get();
+       try {
+           CtClass[] params = Descriptor.getParameterTypes(signature, cp);
+           CtClass newType = cp.get(newTypeName);
+           int paramVar = ca.getMaxLocals();
+           jc.recordParams(newTypeName, params,
+                           true, paramVar, withinStatic());
+           int retVar = jc.recordReturnType(newType, true);
+           jc.recordProceed(new ProceedForNew(newType, newIndex,
+                                              methodIndex));
+
+           /* Is $_ included in the source code?
+            */
+           checkResultValue(newType, statement);
+
+           Bytecode bytecode = jc.getBytecode();
+           storeStack(params, true, paramVar, bytecode);
+           jc.compileStmnt(statement);
+           bytecode.addAload(retVar);
+
+           replace0(pos, bytecode, bytecodeSize);
+       }
+       catch (CompileError e) { throw new CannotCompileException(e); }
+       catch (NotFoundException e) { throw new CannotCompileException(e); }
+       catch (BadBytecode e) {
+           throw new CannotCompileException("broken method");
+       }
+    }
+
+    static class ProceedForNew implements ProceedHandler {
+       CtClass newType;
+       int newIndex, methodIndex;
+
+       ProceedForNew(CtClass nt, int ni, int mi) {
+           newType = nt;
+           newIndex = ni;
+           methodIndex = mi;
+       }
+
+       public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
+           throws CompileError
+       {
+           bytecode.addOpcode(NEW);
+           bytecode.addIndex(newIndex);
+           bytecode.addOpcode(DUP);
+           gen.atMethodCall2(newType, MethodInfo.nameInit,
+                             args, false, true);
+           gen.setType(newType);
+       }
+    }
+}
diff --git a/src/main/javassist/preproc/Assistant.java b/src/main/javassist/preproc/Assistant.java
new file mode 100644 (file)
index 0000000..60d7c4c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.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 javassist.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/src/main/javassist/preproc/Compiler.java b/src/main/javassist/preproc/Compiler.java
new file mode 100644 (file)
index 0000000..60e6a9d
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.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 javassist.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>javassist.preproc.Assistant</code>.
+ *
+ * @see javassist.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/src/main/javassist/reflect/CannotCreateException.java b/src/main/javassist/reflect/CannotCreateException.java
new file mode 100644 (file)
index 0000000..168032f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.reflect;
+
+/**
+ * Signals that <code>ClassMetaobject.newInstance()</code> fails.
+ */
+public class CannotCreateException extends Exception {
+    public CannotCreateException(String s) {
+       super(s);
+    }
+
+    public CannotCreateException(Exception e) {
+       super("by " + e.toString());
+    }
+}
diff --git a/src/main/javassist/reflect/CannotInvokeException.java b/src/main/javassist/reflect/CannotInvokeException.java
new file mode 100644 (file)
index 0000000..455c3e4
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.reflect;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.IllegalAccessException;
+
+/**
+ * Thrown when method invocation using the reflection API has thrown
+ * an exception.
+ *
+ * @see javassist.reflect.Metaobject#trapMethodcall(int, Object[])
+ * @see javassist.reflect.ClassMetaobject#trapMethodcall(int, Object[])
+ * @see javassist.reflect.ClassMetaobject#invoke(Object, int, Object[])
+ */
+public class CannotInvokeException extends RuntimeException {
+    /**
+     * @serial
+     */
+    private Throwable err = null;
+
+    /**
+     * Constructs a CannotInvokeException with an error message.
+     */
+    public CannotInvokeException(String reason) {
+       super(reason);
+    }
+
+    /**
+     * Constructs a CannotInvokeException with an InvocationTargetException.
+     */
+    public CannotInvokeException(InvocationTargetException e) {
+       super("by " + e.getTargetException().toString());
+       err = e.getTargetException();
+    }
+
+    /**
+     * Constructs a CannotInvokeException with an IllegalAccessException.
+     */
+    public CannotInvokeException(IllegalAccessException e) {
+       super("by " + e.toString());
+       err = e;
+    }
+
+    /**
+     * Constructs a CannotInvokeException with an ClassNotFoundException.
+     */
+    public CannotInvokeException(ClassNotFoundException e) {
+       super("by " + e.toString());
+       err = e;
+    }
+}
diff --git a/src/main/javassist/reflect/ClassMetaobject.java b/src/main/javassist/reflect/ClassMetaobject.java
new file mode 100644 (file)
index 0000000..6c0de55
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.reflect;
+
+import java.lang.reflect.*;
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import javassist.CtClass;
+
+/**
+ * A runtime class metaobject.
+ *
+ * <p>A <code>ClassMetaobject</code> is created for every
+ * class of reflective objects.  It can be used to hold values
+ * shared among the reflective objects of the same class.
+ *
+ * @see javassist.reflect.Metaobject
+ */
+public class ClassMetaobject implements Serializable {
+    /**
+     * The base-level methods controlled by a metaobject
+     * are renamed so that they begin with
+     * <code>methodPrefix "_m_"</code>.
+     */
+    static final String methodPrefix = "_m_";
+    static final int methodPrefixLen = 3;
+
+    private Class javaClass;
+    private Constructor[] constructors;
+    private Method[] methods;
+
+    /**
+     * Specifies how a <code>java.lang.Class</code> object is loaded.
+     *
+     * <p>If true, it is loaded by:
+     * <ul><pre>Thread.currentThread().getContextClassLoader().loadClass()</pre></ul>
+     * <p>If false, it is loaded by <code>Class.forName()</code>.
+     * The default value is false.
+     */
+    public static boolean useContextClassLoader = false;
+
+    /**
+     * Constructs a <code>ClassMetaobject</code>.
+     *
+     * @param params   <code>params[0]</code> is the name of the class
+     *                 of the reflective objects.
+     */
+    public ClassMetaobject(String[] params)
+    {
+       try {
+           javaClass = getClassObject(params[0]);
+       }
+       catch (ClassNotFoundException e) {
+           javaClass = null;
+       }
+
+       constructors = javaClass.getConstructors();
+       methods = null;
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+       out.writeUTF(javaClass.getName());
+    }
+
+    private void readObject(ObjectInputStream in)
+       throws IOException, ClassNotFoundException
+    {
+       javaClass = getClassObject(in.readUTF());
+       constructors = javaClass.getConstructors();
+       methods = null;
+    }
+
+    private Class getClassObject(String name) throws ClassNotFoundException {
+       if (useContextClassLoader)
+           return Thread.currentThread().getContextClassLoader()
+                  .loadClass(name);
+       else
+           return Class.forName(name);
+    }
+
+    /**
+     * Obtains the <code>java.lang.Class</code> representing this class.
+     */
+    public final Class getJavaClass() {
+       return javaClass;
+    }
+
+    /**
+     * Obtains the name of this class.
+     */
+    public final String getName() {
+       return javaClass.getName();
+    }
+
+    /**
+     * Returns true if <code>obj</code> is an instance of this class.
+     */
+    public final boolean isInstance(Object obj) {
+       return javaClass.isInstance(obj);
+    }
+
+    /**
+     * Creates a new instance of the class.
+     *
+     * @param args             the arguments passed to the constructor.
+     */
+    public final Object newInstance(Object[] args)
+       throws CannotCreateException
+    {
+       int n = constructors.length;
+       for (int i = 0; i < n; ++i) {
+           try {
+               return constructors[i].newInstance(args);
+           }
+           catch (IllegalArgumentException e) {
+               // try again
+           }
+           catch (InstantiationException e) {
+               throw new CannotCreateException(e);
+           }
+           catch (IllegalAccessException e) {
+               throw new CannotCreateException(e);
+           }
+           catch (InvocationTargetException e) {
+               throw new CannotCreateException(e);
+           }
+       }
+
+       throw new CannotCreateException("no constructor matches");
+    }
+
+    /**
+     * Is invoked when <code>static</code> fields of the base-level
+     * class are read and the runtime system intercepts it.
+     * This method simply returns the value of the field.
+     *
+     * <p>Every subclass of this class should redefine this method.
+     */
+    public Object trapFieldRead(String name) {
+       Class jc = getJavaClass();
+       try {
+           return jc.getField(name).get(null);
+       }
+       catch (NoSuchFieldException e) {
+           throw new RuntimeException(e.toString());
+       }
+       catch (IllegalAccessException e) {
+           throw new RuntimeException(e.toString());
+       }
+    }
+
+    /**
+     * Is invoked when <code>static</code> fields of the base-level
+     * class are modified and the runtime system intercepts it.
+     * This method simply sets the field to the given value.
+     *
+     * <p>Every subclass of this class should redefine this method.
+     */
+    public void trapFieldWrite(String name, Object value) {
+       Class jc = getJavaClass();
+       try {
+           jc.getField(name).set(null, value);
+       }
+       catch (NoSuchFieldException e) {
+           throw new RuntimeException(e.toString());
+       }
+       catch (IllegalAccessException e) {
+           throw new RuntimeException(e.toString());
+       }
+    }
+
+    /**
+     * Invokes a method whose name begins with
+     * <code>methodPrefix "_m_"</code> and the identifier.
+     *
+     * @exception CannotInvokeException                if the invocation fails.
+     */
+    static public Object invoke(Object target, int identifier, Object[] args)
+       throws Throwable
+    {
+       Method[] allmethods = target.getClass().getMethods();
+       int n = allmethods.length;
+       String head = methodPrefix + identifier;
+       for (int i = 0; i < n; ++i)
+           if (allmethods[i].getName().startsWith(head)) {
+               try {
+                   return allmethods[i].invoke(target, args);
+               } catch (java.lang.reflect.InvocationTargetException e) {
+                   throw e.getTargetException();
+               } catch (java.lang.IllegalAccessException e) {
+                   throw new CannotInvokeException(e);
+               }
+           }
+
+       throw new CannotInvokeException("cannot find a method");
+    }
+
+    /**
+     * Is invoked when <code>static</code> methods of the base-level
+     * class are called and the runtime system intercepts it.
+     * This method simply executes the intercepted method invocation
+     * with the original parameters and returns the resulting value.
+     *
+     * <p>Every subclass of this class should redefine this method.
+     */
+    public Object trapMethodcall(int identifier, Object[] args) 
+       throws Throwable
+    {
+       try {
+           Method[] m = getReflectiveMethods();
+           return m[identifier].invoke(null, args);
+       }
+       catch (java.lang.reflect.InvocationTargetException e) {
+           throw e.getTargetException();
+       }
+        catch (java.lang.IllegalAccessException e) {
+           throw new CannotInvokeException(e);
+       }
+    }
+
+    /**
+     * Returns an array of the methods defined on the given reflective
+     * object.  This method is for the internal use only.
+     */
+    public final Method[] getReflectiveMethods() {
+       if (methods != null)
+           return methods;
+
+       Class baseclass = getJavaClass();
+       Method[] allmethods = baseclass.getMethods();
+       int n = allmethods.length;
+       methods = new Method[n];
+       for (int i = 0; i < n; ++i) {
+           Method m = allmethods[i];
+           if (m.getDeclaringClass() == baseclass) {
+               String mname = m.getName();
+               if (mname.startsWith(methodPrefix)) {
+                   int k = 0;
+                   for (int j = methodPrefixLen;; ++j) {
+                       char c = mname.charAt(j);
+                       if ('0' <= c && c <= '9')
+                           k = k * 10 + c - '0';
+                       else
+                           break;
+                   }
+
+                   methods[k] = m;
+               }
+           }
+       }
+
+       return methods;
+    }
+
+    /**
+     * Returns the name of the method specified
+     * by <code>identifier</code>.
+     */
+    public final String getMethodName(int identifier) {
+       String mname = getReflectiveMethods()[identifier].getName();
+       int j = ClassMetaobject.methodPrefixLen;
+       for (;;) {
+           char c = mname.charAt(j++);
+           if (c < '0' || '9' < c)
+               break;
+       }
+
+       return mname.substring(j);
+    }
+
+    /**
+     * Returns an array of <code>Class</code> objects representing the
+     * formal parameter types of the method specified
+     * by <code>identifier</code>.
+     */
+    public final Class[] getParameterTypes(int identifier) {
+       return getReflectiveMethods()[identifier].getParameterTypes();
+    }
+
+    /**
+     * Returns a <code>Class</code> objects representing the
+     * return type of the method specified by <code>identifier</code>.
+     */
+    public final Class getReturnType(int identifier) {
+       return getReflectiveMethods()[identifier].getReturnType();
+    }
+}
diff --git a/src/main/javassist/reflect/Compiler.java b/src/main/javassist/reflect/Compiler.java
new file mode 100644 (file)
index 0000000..6e0b854
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.reflect;
+
+import javassist.CtClass;
+import javassist.ClassPool;
+import java.io.PrintStream;
+
+class CompiledClass {
+    public String classname;
+    public String metaobject;
+    public String classobject;
+}
+
+/**
+ * A bytecode translator for reflection.
+ *
+ * <p>This translator directly modifies class files on a local disk so that
+ * the classes represented by those class files are reflective.
+ * After the modification, the class files can be run with the standard JVM
+ * without <code>javassist.reflect.Loader</code>
+ * or any other user-defined class loader.
+ *
+ * <p>The modified class files are given as the command-line parameters,
+ * which are a sequence of fully-qualified class names followed by options:
+ *
+ * <p><code>-m <i>classname</i></code> : specifies the class of the
+ * metaobjects associated with instances of the class followed by
+ * this option.  The default is <code>javassit.reflect.Metaobject</code>.
+ *
+ * <p><code>-c <i>classname</i></code> : specifies the class of the
+ * class metaobjects associated with instances of the class followed by
+ * this option.  The default is <code>javassit.reflect.ClassMetaobject</code>.
+ *
+ * <p>If a class name is not followed by any options, the class indicated
+ * by that class name is not reflective.
+ * 
+ * <p>For example,
+ * <ul><pre>% java Compiler Dog -m MetaDog -c CMetaDog Cat -m MetaCat Cow
+ * </pre></ul>
+ *
+ * <p>modifies class files <code>Dog.class</code>, <code>Cat.class</code>,
+ * and <code>Cow.class</code>.
+ * The metaobject of a Dog object is a MetaDog object and the class
+ * metaobject is a CMetaDog object.
+ * The metaobject of a Cat object is a MetaCat object but
+ * the class metaobject is a default one.
+ * Cow objects are not reflective.
+ *
+ * <p>Note that if the super class is also made reflective, it must be done
+ * before the sub class.
+ *
+ * @see javassist.reflect.Metaobject
+ * @see javassist.reflect.ClassMetaobject
+ * @see javassist.reflect.Reflection
+ */
+public class Compiler {
+
+    public static void main(String[] args) throws Exception {
+       if (args.length == 0) {
+           help(System.err);
+           return;
+       }
+
+       CompiledClass[] entries = new CompiledClass[args.length];
+       int n = parse(args, entries);
+
+       if (n < 1) {
+           System.err.println("bad parameter.");
+           return;
+       }
+
+       processClasses(entries, n);
+    }
+
+    private static void processClasses(CompiledClass[] entries, int n)
+       throws Exception
+    {
+       Reflection implementor = new Reflection();
+       ClassPool pool = ClassPool.getDefault(implementor);
+
+       for (int i = 0; i < n; ++i) {
+           CtClass c = pool.get(entries[i].classname);
+           if (entries[i].metaobject != null
+                                       || entries[i].classobject != null) {
+               String metaobj, classobj;
+
+               if (entries[i].metaobject == null)
+                   metaobj = "javassist.reflect.Metaobject";
+               else
+                   metaobj = entries[i].metaobject;
+
+               if (entries[i].classobject == null)
+                   classobj = "javassist.reflect.ClassMetaobject";
+               else
+                   classobj = entries[i].classobject;
+
+               if (!implementor.makeReflective(c, pool.get(metaobj),
+                                             pool.get(classobj)))
+                   System.err.println("Warning: " + c.getName()
+                               + " is reflective.  It was not changed.");
+
+               System.err.println(c.getName() + ": " + metaobj + ", "
+                                  + classobj);
+           }
+           else
+               System.err.println(c.getName() + ": not reflective");
+       }
+
+       for (int i = 0; i < n; ++i)
+           pool.writeFile(entries[i].classname);
+    }
+
+    private static int parse(String[] args, CompiledClass[] result) {
+       int n = -1;
+       for (int i = 0; i < args.length; ++i) {
+           String a = args[i];
+           if (a.equals("-m"))
+               if (n < 0 || i + 1 > args.length)
+                   return -1;
+               else
+                   result[n].metaobject = args[++i];
+           else if (a.equals("-c"))
+               if (n < 0 || i + 1 > args.length)
+                   return -1;
+               else
+                   result[n].classobject = args[++i];
+           else if (a.charAt(0) == '-')
+               return -1;
+           else {
+               CompiledClass cc = new CompiledClass();
+               cc.classname = a;
+               cc.metaobject = null;
+               cc.classobject = null;
+               result[++n] = cc;
+           }
+       }
+
+       return n + 1;
+    }
+
+    private static void help(PrintStream out) {
+       out.println("Usage: java javassist.reflect.Compiler");
+       out.println("            (<class> [-m <metaobject>] [-c <class metaobject>])+");
+    }
+}
diff --git a/src/main/javassist/reflect/Loader.java b/src/main/javassist/reflect/Loader.java
new file mode 100644 (file)
index 0000000..5287593
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.reflect;
+
+import javassist.CannotCompileException;
+import javassist.NotFoundException;
+import javassist.ClassPool;
+
+/**
+ * A class loader for reflection.
+ *
+ * <p>To run a program, say <code>MyApp</code>,
+ * including a reflective class,
+ * you must write a start-up program as follows:
+ *
+ * <ul><pre>
+ * public class Main {
+ *   public static void main(String[] args) throws Throwable {
+ *     javassist.reflect.Loader cl
+ *         = (javassist.reflect.Loader)Main.class.getClassLoader();
+ *     cl.makeReflective("Person", "MyMetaobject",
+ *                       "javassist.reflect.ClassMetaobject");
+ *     cl.run("MyApp", args);
+ *   }
+ * }
+ * </pre></ul>
+ *
+ * <p>Then run this program as follows:
+ *
+ * <ul><pre>% java javassist.reflect.Loader Main arg1, ...</pre></ul>
+ *
+ * <p>This command runs <code>Main.main()</code> with <code>arg1</code>, ...
+ * and <code>Main.main()</code> runs <code>MyApp.main()</code> with
+ * <code>arg1</code>, ...
+ * The <code>Person</code> class is modified
+ * to be a reflective class.  Method calls on a <code>Person</code>
+ * object are intercepted by an instance of <code>MyMetaobject</code>.
+ *
+ * <p>Also, you can run <code>MyApp</code> in a slightly different way:
+ *
+ * <ul><pre>
+ * public class Main2 {
+ *   public static void main(String[] args) throws Throwable {
+ *     javassist.reflect.Loader cl = new javassist.reflect.Loader();
+ *     cl.makeReflective("Person", "MyMetaobject",
+ *                       "javassist.reflect.ClassMetaobject");
+ *     cl.run("MyApp", args);
+ *   }
+ * }
+ * </pre></ul>
+ *
+ * <p>This program is run as follows:
+ *
+ * <ul><pre>% java Main2 arg1, ...</code>
+ *
+ * <p>The difference from the former one is that the class <code>Main</code>
+ * is loaded by <code>javassist.reflect.Loader</code> whereas the class
+ * <code>Main2</code> is not.  Thus, <code>Main</code> belongs
+ * to the same name space (security domain) as <code>MyApp</code>
+ * whereas <code>Main2</code> does not; <code>Main2</code> belongs
+ * to the same name space as <code>javassist.reflect.Loader</code>.
+ * For more details,
+ * see the notes in the manual page of <code>javassist.Loader</code>.
+ *
+ * <p>The class <code>Main2</code> is equivalent to this class:
+ *
+ * <ul><pre>
+ * public class Main3 {
+ *   public static void main(String[] args) throws Throwable {
+ *     Reflection reflection = new Reflection();
+ *     javassist.Loader cl
+ *         = new javassist.Loader(ClassPool.getDefault(reflection));
+ *     reflection.makeReflective("Person", "MyMetaobject",
+ *                               "javassist.reflect.ClassMetaobject");
+ *     cl.run("MyApp", args);
+ *   }
+ * }
+ * </pre></ul>
+ *
+ * <p><b>Note:</b>
+ *
+ * <p><code>javassist.reflect.Loader</code> does not make a class reflective
+ * if that class is in a <code>java.*</code> or
+ * <code>javax.*</code> pacakge because of the specifications
+ * on the class loading algorithm of Java.  The JVM does not allow to
+ * load such a system class with a user class loader.
+ *
+ * <p>To avoid this limitation, those classes should be statically
+ * modified with <code>javassist.reflect.Compiler</code> and the original
+ * class files should be replaced.
+ *
+ * @see javassist.reflect.Reflection
+ * @see javassist.reflect.Compiler
+ * @see javassist.Loader
+ */
+public class Loader extends javassist.Loader {
+    protected Reflection reflection;
+
+    /**
+     * Loads a class with an instance of <code>Loader</code>
+     * and calls <code>main()</code> in that class.
+     *
+     * @param args[0]          class name to be loaded.
+     * @param args[1-n]                parameters passed to <code>main()</code>.
+     */
+    public static void main(String[] args) throws Throwable {
+       Loader cl = new Loader();
+       cl.run(args);
+    }
+
+    /**
+     * Constructs a new class loader.
+     */
+    public Loader() {
+       super();
+       delegateLoadingOf("javassist.reflect.Loader");
+
+       reflection = new Reflection();
+       setClassPool(ClassPool.getDefault(reflection));
+    }
+
+    /**
+     * Produces a reflective class.
+     * If the super class is also made reflective, it must be done
+     * before the sub class.
+     *
+     * @param clazz            the reflective class.
+     * @param metaobject       the class of metaobjects.
+     *                         It must be a subclass of
+     *                         <code>Metaobject</code>.
+     * @param metaclass                the class of the class metaobject.
+     *                         It must be a subclass of
+     *                         <code>ClassMetaobject</code>.
+     * @return <code>false</code>      if the class is already reflective.
+     *
+     * @see javassist.reflect.Metaobject
+     * @see javassist.reflect.ClassMetaobject
+     */
+    public boolean makeReflective(String clazz,
+                                 String metaobject, String metaclass)
+       throws CannotCompileException, NotFoundException
+    {
+       return reflection.makeReflective(clazz, metaobject, metaclass);
+    }
+}
diff --git a/src/main/javassist/reflect/Metalevel.java b/src/main/javassist/reflect/Metalevel.java
new file mode 100644 (file)
index 0000000..96e53d5
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.reflect;
+
+/**
+ * An interface to access a metaobject and a class metaobject.
+ * This interface is implicitly implemented by the reflective
+ * class.
+ */
+public interface Metalevel {
+    /**
+     * Obtains the class metaobject associated with this object.
+     */
+    ClassMetaobject _getClass();
+
+    /**
+     * Obtains the metaobject associated with this object.
+     */
+    Metaobject _getMetaobject();
+
+    /**
+     * Changes the metaobject associated with this object.
+     */
+    void _setMetaobject(Metaobject m);
+}
diff --git a/src/main/javassist/reflect/Metaobject.java b/src/main/javassist/reflect/Metaobject.java
new file mode 100644 (file)
index 0000000..8f1d02b
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.reflect;
+
+import java.lang.reflect.Method;
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * A runtime metaobject.
+ *
+ * <p>A <code>Metaobject</code> is created for
+ * every object at the base level.  A different reflective object is
+ * associated with a different metaobject.
+ *
+ * <p>The metaobject intercepts method calls
+ * on the reflective object at the base-level.  To change the behavior
+ * of the method calls, a subclass of <code>Metaobject</code>
+ * should be defined.
+ *
+ * @see javassist.reflect.ClassMetaobject
+ */
+public class Metaobject implements Serializable {
+    protected ClassMetaobject classmetaobject;
+    protected Metalevel baseobject;
+    protected Method[] methods;
+
+    /**
+     * Constructs a <code>Metaobject</code>.  The metaobject is
+     * constructed before the constructor is called on the base-level
+     * object.
+     *
+     * @param self     the object that this metaobject is associated with.
+     * @param args     the parameters passed to the constructor of
+     *                 <code>self</code>.
+     */
+    public Metaobject(Object self, Object[] args) {
+       baseobject = (Metalevel)self;
+       classmetaobject = baseobject._getClass();
+       methods = classmetaobject.getReflectiveMethods();
+    }
+
+    /**
+     * Constructs a <code>Metaobject</code> without initialization.
+     * If calling this constructor, a subclass should be responsible
+     * for initialization.
+     */
+    protected Metaobject() {
+       baseobject = null;
+       classmetaobject = null;
+       methods = null;
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+       out.writeObject(baseobject);
+    }
+
+    private void readObject(ObjectInputStream in)
+       throws IOException, ClassNotFoundException
+    {
+       baseobject = (Metalevel)in.readObject();
+       classmetaobject = baseobject._getClass();
+       methods = classmetaobject.getReflectiveMethods();
+    }
+
+    /**
+     * Obtains the class metaobject associated with this metaobject.
+     *
+     * @see javassist.reflect.ClassMetaobject
+     */
+    public final ClassMetaobject getClassMetaobject() {
+       return classmetaobject;
+    }
+
+    /**
+     * Obtains the object controlled by this metaobject.
+     */
+    public final Object getObject() {
+       return baseobject;
+    }
+
+    /**
+     * Changes the object controlled by this metaobject.
+     *
+     * @param self     the object
+     */
+    public final void setObject(Object self) {
+       baseobject = (Metalevel)self;
+       classmetaobject = baseobject._getClass();
+       methods = classmetaobject.getReflectiveMethods();
+
+       // call _setMetaobject() after the metaobject is settled.
+       baseobject._setMetaobject(this);
+    }
+
+    /**
+     * Returns the name of the method specified
+     * by <code>identifier</code>.
+     */
+    public final String getMethodName(int identifier) {
+        String mname = methods[identifier].getName();
+       int j = ClassMetaobject.methodPrefixLen;
+       for (;;) {
+           char c = mname.charAt(j++);
+           if (c < '0' || '9' < c)
+               break;
+       }
+
+       return mname.substring(j);
+    }
+
+    /**
+     * Returns an array of <code>Class</code> objects representing the
+     * formal parameter types of the method specified
+     * by <code>identifier</code>.
+     */
+    public final Class[] getParameterTypes(int identifier) {
+       return methods[identifier].getParameterTypes();
+    }
+
+    /**
+     * Returns a <code>Class</code> objects representing the
+     * return type of the method specified by <code>identifier</code>.
+     */
+    public final Class getReturnType(int identifier) {
+       return methods[identifier].getReturnType();
+    }
+
+    /**
+     * Is invoked when public fields of the base-level
+     * class are read and the runtime system intercepts it.
+     * This method simply returns the value of the field.
+     *
+     * <p>Every subclass of this class should redefine this method.
+     */
+    public Object trapFieldRead(String name) {
+       Class jc = getClassMetaobject().getJavaClass();
+       try {
+           return jc.getField(name).get(getObject());
+       }
+       catch (NoSuchFieldException e) {
+           throw new RuntimeException(e.toString());
+       }
+       catch (IllegalAccessException e) {
+           throw new RuntimeException(e.toString());
+       }
+    }
+
+    /**
+     * Is invoked when public fields of the base-level
+     * class are modified and the runtime system intercepts it.
+     * This method simply sets the field to the given value.
+     *
+     * <p>Every subclass of this class should redefine this method.
+     */
+    public void trapFieldWrite(String name, Object value) {
+       Class jc = getClassMetaobject().getJavaClass();
+       try {
+           jc.getField(name).set(getObject(), value);
+       }
+       catch (NoSuchFieldException e) {
+           throw new RuntimeException(e.toString());
+       }
+       catch (IllegalAccessException e) {
+           throw new RuntimeException(e.toString());
+       }
+    }
+
+    /**
+     * Is invoked when base-level method invocation is intercepted.
+     * This method simply executes the intercepted method invocation
+     * with the original parameters and returns the resulting value.
+     *
+     * <p>Every subclass of this class should redefine this method.
+     *
+     * <p>Note: this method is not invoked if the base-level method
+     * is invoked by a constructor in the super class.  For example,
+     *
+     * <ul><pre>abstract class A {
+     *   abstract void initialize();
+     *   A() {
+     *       initialize();    // not intercepted
+     *   }
+     * }
+     *
+     * class B extends A {
+     *   void initialize() { System.out.println("initialize()"); }
+     *   B() {
+     *       super();
+     *       initialize();    // intercepted
+     *   }
+     * }</pre></ul>
+     *
+     * <p>if an instance of B is created,
+     * the invocation of initialize() in B is intercepted only once.
+     * The first invocation by the constructor in A is not intercepted.
+     * This is because the link between a base-level object and a
+     * metaobject is not created until the execution of a
+     * constructor of the super class finishes.
+     */
+    public Object trapMethodcall(int identifier, Object[] args) 
+       throws Throwable
+    {
+       try {
+           return methods[identifier].invoke(getObject(), args);
+       }
+       catch (java.lang.reflect.InvocationTargetException e) {
+           throw e.getTargetException();
+       }
+        catch (java.lang.IllegalAccessException e) {
+           throw new CannotInvokeException(e);
+       }
+    }
+}
diff --git a/src/main/javassist/reflect/Reflection.java b/src/main/javassist/reflect/Reflection.java
new file mode 100644 (file)
index 0000000..f76b396
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.reflect;
+
+import javassist.*;
+import java.io.IOException;
+import javassist.CtMethod.ConstParameter;
+
+/**
+ * The class implementing the reflection mechanism.
+ *
+ * <p>This class is used with <code>ClassPool</code>.
+ * Note that it implements an interface <code>javassist.Translator</code>.
+ *
+ * <p>If a class is reflective,
+ * then all the method invocations on every
+ * instance of that class are intercepted by the runtime
+ * metaobject controlling that instance.
+ * To do this, the original class file representing a reflective class:
+ *
+ * <ul><pre>
+ * class Person {
+ *   public int f(int i) { return i + 1; }
+ *   public int value;
+ * }
+ * </pre></ul>
+ *
+ * <p>is modified so that it represents a class:
+ *
+ * <ul><pre>
+ * class Person implements Metalevel {
+ *   public int _original_f(int i) { return i + 1; }
+ *   public int f(int i) { <i>delegate to the metaobject</i> }
+ *
+ *   public int value;
+ *   public int _r_value() { <i>read "value"</i> }
+ *   public void _w_value(int v) { <i>write "value"</i> }
+ *
+ *   public ClassMetaobject _getClass() { <i>return a class metaobject</i> }
+ *   public Metaobject _getMetaobject() { <i>return a metaobject</i> }
+ *   public void _setMetaobject(Metaobject m) { <i>change a metaobject</i> }
+ * }
+ * </pre></ul>
+ *
+ * @see javassist.reflect.ClassMetaobject
+ * @see javassist.reflect.Metaobject
+ * @see javassist.reflect.Loader
+ * @see javassist.reflect.Compiler
+ * @see javassist.ClassPool
+ * @see javassist.Translator
+ */
+public class Reflection implements Translator {
+
+    static final String classobjectField = "_classobject";
+    static final String classobjectAccessor = "_getClass";
+    static final String metaobjectField = "_metaobject";
+    static final String metaobjectGetter = "_getMetaobject";
+    static final String metaobjectSetter = "_setMetaobject";
+    static final String readPrefix = "_r_";
+    static final String writePrefix = "_w_";
+
+    protected CtMethod trapMethod, trapStaticMethod;
+    protected CtMethod trapRead, trapWrite;
+    protected CtClass[] readParam;
+
+    protected ClassPool classPool;
+    protected CodeConverter converter;
+
+    private boolean isExcluded(String name) {
+       return name.startsWith(ClassMetaobject.methodPrefix)
+           || name.equals(classobjectAccessor)
+           || name.equals(metaobjectSetter)
+           || name.equals(metaobjectGetter)
+           || name.startsWith(readPrefix)
+           || name.startsWith(writePrefix);
+    }
+
+    /**
+     * Constructs a new <code>Reflection</code> object.
+     */
+    public Reflection() {
+       classPool = null;
+       converter = new CodeConverter();
+    }
+
+    /**
+     * Initializes.
+     */
+    public void start(ClassPool pool) throws NotFoundException {
+       classPool = pool;
+       final String msg
+           = "javassist.reflect.Sample is not found or broken.";
+       try {
+           CtClass c = classPool.get("javassist.reflect.Sample");
+           trapMethod = c.getDeclaredMethod("trap");
+           trapStaticMethod = c.getDeclaredMethod("trapStatic");
+           trapRead = c.getDeclaredMethod("trapRead");
+           trapWrite = c.getDeclaredMethod("trapWrite");
+           readParam
+               = new CtClass[] { classPool.get("java.lang.Object") };
+       }
+       catch (NotFoundException e) {
+           throw new RuntimeException(msg);
+       }
+    }
+
+    /**
+     * Inserts hooks for intercepting accesses to the fields declared
+     * in reflective classes.
+     */
+    public void onWrite(ClassPool pool, String classname)
+       throws CannotCompileException, NotFoundException
+    {
+       CtClass c = pool.get(classname);
+       c.instrument(converter);
+    }
+
+    /**
+     * Produces a reflective class.
+     * If the super class is also made reflective, it must be done
+     * before the sub class.
+     *
+     * @param classname                the name of the reflective class
+     * @param metaobject       the class name of metaobjects.
+     * @param metaclass                the class name of the class metaobject.
+     * @return <code>false</code>      if the class is already reflective.
+     *
+     * @see javassist.reflect.Metaobject
+     * @see javassist.reflect.ClassMetaobject
+     */
+    public boolean makeReflective(String classname,
+                                 String metaobject, String metaclass)
+       throws CannotCompileException, NotFoundException
+    {
+       return makeReflective(classPool.get(classname),
+                             classPool.get(metaobject),
+                             classPool.get(metaclass));
+    }
+
+    /**
+     * Produces a reflective class.
+     * If the super class is also made reflective, it must be done
+     * before the sub class.
+     *
+     * @param clazz            the reflective class.
+     * @param metaobject       the class of metaobjects.
+     *                         It must be a subclass of
+     *                         <code>Metaobject</code>.
+     * @param metaclass                the class of the class metaobject.
+     *                         It must be a subclass of
+     *                         <code>ClassMetaobject</code>.
+     * @return <code>false</code>      if the class is already reflective.
+     *
+     * @see javassist.reflect.Metaobject
+     * @see javassist.reflect.ClassMetaobject
+     */
+    public boolean makeReflective(Class clazz,
+                                 Class metaobject, Class metaclass)
+       throws CannotCompileException, NotFoundException
+    {
+       return makeReflective(clazz.getName(), metaobject.getName(),
+                             metaclass.getName());
+    }
+
+    /**
+     * Produces a reflective class.  It modifies the given
+     * <code>CtClass</code> object and makes it reflective.
+     * If the super class is also made reflective, it must be done
+     * before the sub class.
+     *
+     * @param clazz            the reflective class.
+     * @param metaobject       the class of metaobjects.
+     *                         It must be a subclass of
+     *                         <code>Metaobject</code>.
+     * @param metaclass                the class of the class metaobject.
+     *                         It must be a subclass of
+     *                         <code>ClassMetaobject</code>.
+     * @return <code>false</code>      if the class is already reflective.
+     *
+     * @see javassist.reflect.Metaobject
+     * @see javassist.reflect.ClassMetaobject
+     */
+    public boolean makeReflective(CtClass clazz,
+                                 CtClass metaobject, CtClass metaclass)
+       throws CannotCompileException, NotFoundException
+    {
+       registerReflectiveClass(clazz);
+       return modifyClassfile(clazz, metaobject, metaclass);
+    }
+
+    /**
+     * Registers a reflective class.  The field accesses to the instances
+     * of this class are instrumented.
+     */
+    private void registerReflectiveClass(CtClass clazz) {
+       CtField[] fs = clazz.getDeclaredFields();
+       for (int i = 0; i < fs.length; ++i) {
+           CtField f = fs[i];
+           int mod = f.getModifiers();
+           if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) {
+               String name = f.getName();
+               converter.replaceFieldRead(f, clazz, readPrefix + name);
+               converter.replaceFieldWrite(f, clazz, writePrefix + name);
+           }
+       }
+    }
+
+    private boolean modifyClassfile(CtClass clazz, CtClass metaobject,
+                                   CtClass metaclass)
+       throws CannotCompileException, NotFoundException
+    {
+       if (clazz.getAttribute("Reflective") != null)
+           return false;       // this is already reflective.
+       else
+           clazz.setAttribute("Reflective", new byte[0]);
+
+       CtClass mlevel = classPool.get("javassist.reflect.Metalevel");
+       boolean addMeta = !clazz.subtypeOf(mlevel);
+       if (addMeta)
+           clazz.addInterface(mlevel);
+
+       processMethods(clazz, addMeta);
+       processFields(clazz);
+
+       CtField f;
+       if (addMeta) {
+           f = new CtField(classPool.get("javassist.reflect.Metaobject"),
+                           metaobjectField, clazz);
+           f.setModifiers(Modifier.PROTECTED);
+           clazz.addField(f, CtField.Initializer.byNewWithParams(metaobject));
+
+           clazz.addMethod(CtNewMethod.getter(metaobjectGetter, f));
+           clazz.addMethod(CtNewMethod.setter(metaobjectSetter, f));
+       }
+
+       f = new CtField(classPool.get("javassist.reflect.ClassMetaobject"),
+                       classobjectField, clazz);
+       f.setModifiers(Modifier.PRIVATE | Modifier.STATIC);
+       clazz.addField(f, CtField.Initializer.byNew(metaclass,
+                                       new String[] { clazz.getName() }));
+
+       clazz.addMethod(CtNewMethod.getter(classobjectAccessor, f));
+       return true;
+    }
+
+    private void processMethods(CtClass clazz, boolean dontSearch)
+       throws CannotCompileException, NotFoundException
+    {
+       CtMethod[] ms = clazz.getMethods();
+       int identifier = 0;
+       for (int i = 0; i < ms.length; ++i) {
+           CtMethod m = ms[i];
+           int mod = m.getModifiers();
+           if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod))
+               processMethods0(mod, clazz, m, i, dontSearch);
+       }
+    }
+
+    private void processMethods0(int mod, CtClass clazz,
+                       CtMethod m, int identifier, boolean dontSearch)
+       throws CannotCompileException, NotFoundException
+    {
+       CtMethod body;
+       String name = m.getName();
+
+       if (isExcluded(name))   // internally-used method inherited
+           return;             // from a reflective class.
+
+       CtMethod m2;
+       if (m.getDeclaringClass() == clazz) {
+           if (Modifier.isNative(mod))
+               return;
+
+           m2 = m;
+       }
+       else {
+           if (Modifier.isFinal(mod))
+               return;
+
+           mod &= ~Modifier.NATIVE;
+           m2 = CtNewMethod.delegator(findOriginal(m, dontSearch), clazz);
+           m2.setModifiers(mod);
+           clazz.addMethod(m2);
+       }
+
+       m2.setName(ClassMetaobject.methodPrefix + identifier
+                     + "_" + name);
+
+       if (Modifier.isStatic(mod))
+           body = trapStaticMethod;
+       else
+           body = trapMethod;
+
+       CtMethod wmethod
+           = CtNewMethod.wrapped(m.getReturnType(), name,
+                                 m.getParameterTypes(), m.getExceptionTypes(),
+                                 body, ConstParameter.integer(identifier),
+                                 clazz);
+       wmethod.setModifiers(mod);
+       clazz.addMethod(wmethod);
+    }
+
+    private CtMethod findOriginal(CtMethod m, boolean dontSearch)
+       throws NotFoundException
+    {
+       if (dontSearch)
+           return m;
+
+       String name = m.getName();
+       CtMethod[] ms = m.getDeclaringClass().getDeclaredMethods();
+       for (int i = 0; i < ms.length; ++i) {
+           String orgName = ms[i].getName();
+           if (orgName.endsWith(name)
+               && orgName.startsWith(ClassMetaobject.methodPrefix)
+               && ms[i].getSignature().equals(m.getSignature()))
+               return ms[i];
+       }
+
+       return m;
+    }
+
+    private void processFields(CtClass clazz)
+       throws CannotCompileException, NotFoundException
+    {
+       CtField[] fs = clazz.getDeclaredFields();
+       for (int i = 0; i < fs.length; ++i) {
+           CtField f = fs[i];
+           int mod = f.getModifiers();
+           if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) {
+               mod |= Modifier.STATIC;
+               String name = f.getName();
+               CtClass ftype = f.getType();
+               CtMethod wmethod
+                   = CtNewMethod.wrapped(ftype, readPrefix + name,
+                                         readParam, null, trapRead,
+                                         ConstParameter.string(name),
+                                         clazz);
+               wmethod.setModifiers(mod);
+               clazz.addMethod(wmethod);
+               CtClass[] writeParam = new CtClass[2];
+               writeParam[0] = classPool.get("java.lang.Object");
+               writeParam[1] = ftype;
+               wmethod = CtNewMethod.wrapped(CtClass.voidType,
+                               writePrefix + name,
+                               writeParam, null, trapWrite,
+                               ConstParameter.string(name), clazz);
+               wmethod.setModifiers(mod);
+               clazz.addMethod(wmethod);
+           }
+       }
+    }
+}
diff --git a/src/main/javassist/reflect/Sample.java b/src/main/javassist/reflect/Sample.java
new file mode 100644 (file)
index 0000000..0b760de
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.reflect;
+
+/**
+ * A template used for defining a reflective class.
+ */
+public class Sample {
+    private Metaobject _metaobject;
+    private static ClassMetaobject _classobject;
+
+    public Object trap(Object[] args, int identifier) throws Throwable {
+       Metaobject mobj;
+       mobj = _metaobject;
+       if (mobj == null)
+           return ClassMetaobject.invoke(this, identifier, args);
+       else
+           return mobj.trapMethodcall(identifier, args);
+    }
+
+    public static Object trapStatic(Object[] args, int identifier)
+       throws Throwable
+    {
+       return _classobject.trapMethodcall(identifier, args);
+    }
+
+    public static Object trapRead(Object[] args, String name) {
+       if (args[0] == null)
+           return _classobject.trapFieldRead(name);
+       else
+           return ((Metalevel)args[0])._getMetaobject().trapFieldRead(name);
+    }
+
+    public static Object trapWrite(Object[] args, String name) {
+       Metalevel base = (Metalevel)args[0];
+       if (base == null)
+           _classobject.trapFieldWrite(name, args[1]);
+       else
+           base._getMetaobject().trapFieldWrite(name, args[1]);
+
+       return null;
+    }
+}
diff --git a/src/main/javassist/rmi/AppletServer.java b/src/main/javassist/rmi/AppletServer.java
new file mode 100644 (file)
index 0000000..2b8c30e
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.rmi;
+
+import java.io.*;
+import javassist.web.*;
+import javassist.CannotCompileException;
+import javassist.NotFoundException;
+import javassist.ClassPool;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * An AppletServer object is a web server that an ObjectImporter
+ * communicates with.  It makes the objects specified by
+ * <code>exportObject()</code> remotely accessible from applets.
+ * If the classes of the exported objects are requested by the client-side
+ * JVM, this web server sends proxy classes for the requested classes.
+ *
+ * @see javassist.rmi.ObjectImporter
+ */
+public class AppletServer extends Webserver {
+    private StubGenerator stubGen;
+    private Hashtable exportedNames;
+    private Vector exportedObjects;
+
+    private static final byte[] okHeader
+                               = "HTTP/1.0 200 OK\r\n\r\n".getBytes();
+
+    /**
+     * Constructs a web server.
+     *
+     * @param port     port number
+     */
+    public AppletServer(String port)
+       throws IOException, NotFoundException, CannotCompileException
+    {
+       this(Integer.parseInt(port));
+    }
+
+    /**
+     * Constructs a web server.
+     *
+     * @param port     port number
+     */
+    public AppletServer(int port)
+       throws IOException, NotFoundException, CannotCompileException
+    {
+       this(ClassPool.getDefault(new StubGenerator()), port);
+    }
+
+    /**
+     * Constructs a web server.
+     *
+     * @param port     port number
+     * @param src      the source of classs files.
+     */
+    public AppletServer(int port, ClassPool src)
+       throws IOException, NotFoundException, CannotCompileException
+    {
+       this(new ClassPool(src, new StubGenerator()), port);
+    }
+
+    private AppletServer(ClassPool loader, int port)
+       throws IOException, NotFoundException, CannotCompileException
+    {
+       super(port);
+       exportedNames = new Hashtable();
+       exportedObjects = new Vector();
+       stubGen = (StubGenerator)loader.getTranslator();
+       setClassPool(loader);
+    }
+
+    /**
+     * Begins the HTTP service.
+     */
+    public void run() {
+       super.run();
+    }
+
+    /**
+     * Exports an object.
+     * This method produces the bytecode of the proxy class used
+     * to access the exported object.  A remote applet can load
+     * the proxy class and call a method on the exported object.
+     *
+     * @param name     the name used for looking the object up.
+     * @param object   the exported object.
+     * @return         the object identifier
+     *
+     * @see javassist.rmi.ObjectImporter#lookupObject(String)
+     */
+    public synchronized int exportObject(String name, Object obj)
+       throws CannotCompileException
+    {
+       Class clazz = obj.getClass();
+       ExportedObject eo = new ExportedObject();
+       eo.object = obj;
+       eo.methods = clazz.getMethods();
+       exportedObjects.addElement(eo);
+       eo.identifier = exportedObjects.size() - 1;
+       if (name != null)
+           exportedNames.put(name, eo);
+
+       try {
+           stubGen.makeProxyClass(clazz);
+       }
+       catch (NotFoundException e) {
+           throw new CannotCompileException(e);
+       }
+
+       return eo.identifier;
+    }
+
+    /**
+     * Processes a request from a web browser (an ObjectImporter).
+     */
+    public void doReply(InputStream in, OutputStream out, String cmd)
+       throws IOException, BadHttpRequest
+    {
+       if (cmd.startsWith("POST /rmi "))
+           processRMI(in, out);
+       else if (cmd.startsWith("POST /lookup "))
+           lookupName(cmd, in, out);
+       else
+           super.doReply(in, out, cmd);
+    }
+
+    private void processRMI(InputStream ins, OutputStream outs)
+       throws IOException
+    {
+       ObjectInputStream in = new ObjectInputStream(ins);
+
+       int objectId = in.readInt();
+       int methodId = in.readInt();
+       Exception err = null;
+       Object rvalue = null;
+       try {
+           ExportedObject eo
+               = (ExportedObject)exportedObjects.elementAt(objectId);
+           Object[] args = readParameters(in);
+           rvalue = convertRvalue(eo.methods[methodId].invoke(eo.object,
+                                                              args));
+       }
+       catch(Exception e) {
+           err = e;
+           logging2(e.toString());
+       }
+
+       outs.write(okHeader);
+       ObjectOutputStream out = new ObjectOutputStream(outs);
+       if (err != null) {
+           out.writeBoolean(false);
+           out.writeUTF(err.toString());
+       }
+       else
+           try {
+               out.writeBoolean(true);
+               out.writeObject(rvalue);
+           }
+           catch (NotSerializableException e) {
+               logging2(e.toString());
+           }
+           catch (InvalidClassException e) {
+               logging2(e.toString());
+           }
+
+       out.flush();
+       out.close();
+       in.close();
+    }
+
+    private Object[] readParameters(ObjectInputStream in)
+       throws IOException, ClassNotFoundException
+    {
+       int n = in.readInt();
+       Object[] args = new Object[n];
+       for (int i = 0; i < n; ++i) {
+           Object a = in.readObject();
+           if (a instanceof RemoteRef) {
+               RemoteRef ref = (RemoteRef)a;
+               ExportedObject eo
+                   = (ExportedObject)exportedObjects.elementAt(ref.oid);
+               a = eo.object;
+           }
+
+           args[i] = a;
+       }
+
+       return args;
+    }
+
+    private Object convertRvalue(Object rvalue)
+       throws CannotCompileException
+    {
+       if (rvalue == null)
+           return null;        // the return type is void.
+
+       String classname = rvalue.getClass().getName();
+       if (stubGen.isProxyClass(classname))
+           return new RemoteRef(exportObject(null, rvalue), classname);
+       else
+           return rvalue;
+    }
+
+    private void lookupName(String cmd, InputStream ins, OutputStream outs)
+       throws IOException
+    {
+       ObjectInputStream in = new ObjectInputStream(ins);
+       String name = DataInputStream.readUTF(in);
+       ExportedObject found = (ExportedObject)exportedNames.get(name);
+       outs.write(okHeader);
+       ObjectOutputStream out = new ObjectOutputStream(outs);
+       if (found == null) {
+           logging2(name + "not found.");
+           out.writeInt(-1);           // error code
+           out.writeUTF("error");
+       }
+       else {
+           logging2(name);
+           out.writeInt(found.identifier);
+           out.writeUTF(found.object.getClass().getName());
+       }
+
+       out.flush();
+       out.close();
+       in.close();
+    }
+}
+
+class ExportedObject {
+    public int identifier;
+    public Object object;
+    public Method[] methods;
+}
diff --git a/src/main/javassist/rmi/ObjectImporter.java b/src/main/javassist/rmi/ObjectImporter.java
new file mode 100644 (file)
index 0000000..19b6cb4
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.rmi;
+
+import java.io.*;
+import java.net.*;
+import java.awt.*;
+import java.applet.Applet;
+import java.lang.reflect.*;
+
+/**
+ * The object importer enables applets to call a method on a remote
+ * object running on the <code>Webserver</code>.
+ *
+ * <p>To access the remote
+ * object, the applet first calls <code>lookupObject()</code> and
+ * obtains a proxy object, which is a reference to that object.
+ * The class name of the proxy object is identical to that of
+ * the remote object.
+ * The proxy object provides the same set of methods as the remote object.
+ * If one of the methods is invoked on the proxy object,
+ * the invocation is delegated to the remote object.
+ * From the viewpoint of the applet, therefore, the two objects are
+ * identical. The applet can access the object on the server
+ * with the regular Java syntax without concern about the actual
+ * location.
+ *
+ * <p>The methods remotely called by the applet must be <code>public</code>.
+ * This is true even if the applet's class and the remote object's classs
+ * belong to the same package.
+ *
+ * <p>If class X is a class of remote objects, a subclass of X must be
+ * also a class of remote objects.  On the other hand, this restriction
+ * is not applied to the superclass of X.  The class X does not have to
+ * contain a constructor taking no arguments.
+ *
+ * <p>The parameters to a remote method is passed in the <i>call-by-value</i>
+ * manner.  Thus all the parameter classes must implement
+ * <code>java.io.Serializable</code>.  However, if the parameter is the
+ * proxy object, the reference to the remote object instead of a copy of
+ * the object is passed to the method.
+ *
+ * <p>Because of the limitations of the current implementation,
+ * <ul>
+ * <li>The parameter objects cannot contain the proxy
+ * object as a field value.
+ * <li>If class <code>C</code> is of the remote object, then
+ * the applet cannot instantiate <code>C</code> locally or remotely.
+ * </ul>
+ *
+ * <p>All the exceptions thrown by the remote object are converted
+ * into <code>RemoteException</code>.  Since this exception is a subclass
+ * of <code>RuntimeException</code>, the caller method does not need
+ * to catch the exception.  However, good programs should catch
+ * the <code>RuntimeException</code>.
+ *
+ * @see javassist.rmi.AppletServer
+ * @see javassist.rmi.RemoteException
+ * @see javassist.web.Viewer
+ */
+public class ObjectImporter implements java.io.Serializable {
+    private final byte[] endofline = { 0x0d, 0x0a };
+    private String servername, orgServername;
+    private int port, orgPort;
+
+    private byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes();
+    private byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes();
+
+    /**
+     * Constructs an object importer.
+     *
+     * <p>Remote objects are imported from the web server that the given
+     * applet has been loaded from.
+     *
+     * @param applet   the applet loaded from the <code>Webserver</code>.
+     */
+    public ObjectImporter(Applet applet) {
+       URL codebase = applet.getCodeBase();
+       orgServername = servername = codebase.getHost();
+       orgPort = port = codebase.getPort();
+    }
+
+    /**
+     * Constructs an object importer.
+     *
+     * <p>If you run a program with <code>javassist.web.Viewer</code>,
+     * you can construct an object importer as follows:
+     *
+     * <ul><pre>
+     * Viewer v = (Viewer)this.getClass().getClassLoader();
+     * ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort());
+     * </pre></ul>
+     *
+     * @see javassist.web.Viewer
+     */
+    public ObjectImporter(String servername, int port) {
+       this.orgServername = this.servername = servername;
+       this.orgPort = this.port = port;
+    }
+
+    /**
+     * Finds the object exported by a server with the specified name.
+     * If the object is not found, this method returns null.
+     *
+     * @param name     the name of the exported object.
+     * @return         the proxy object or null.
+     */
+    public Object getObject(String name) {
+       try {
+           return lookupObject(name);
+       }
+       catch (ObjectNotFoundException e) {
+           return null;
+       }
+    }
+
+    /**
+     * Sets an http proxy server.  After this method is called, the object
+     * importer connects a server through the http proxy server.
+     */
+    public void setHttpProxy(String host, int port) {
+       String proxyHeader = "POST http://" + orgServername + ":" + orgPort;
+       String cmd = proxyHeader + "/lookup HTTP/1.0";
+       lookupCommand = cmd.getBytes();
+       cmd = proxyHeader + "/rmi HTTP/1.0";
+       rmiCommand = cmd.getBytes();
+       this.servername = host;
+       this.port = port;
+    }
+
+    /**
+     * Finds the object exported by the server with the specified name.
+     * It sends a POST request to the server (via an http proxy server
+     * if needed).
+     *
+     * @param name     the name of the exported object.
+     * @return         the proxy object.
+     */
+    public Object lookupObject(String name) throws ObjectNotFoundException
+    {
+       try {
+           Socket sock = new Socket(servername, port);
+           OutputStream out = sock.getOutputStream();
+           out.write(lookupCommand);
+           out.write(endofline);
+           out.write(endofline);
+
+           ObjectOutputStream dout = new ObjectOutputStream(out);
+           dout.writeUTF(name);
+           dout.flush();
+
+           InputStream in = new BufferedInputStream(sock.getInputStream());
+           skipHeader(in);
+           ObjectInputStream din = new ObjectInputStream(in);
+           int n = din.readInt();
+           String classname = din.readUTF();
+           din.close();
+           dout.close();
+           sock.close();
+
+           if (n >= 0)
+               return createProxy(n, classname);
+       }
+       catch (Exception e) {
+           e.printStackTrace();
+           throw new ObjectNotFoundException(name, e);
+       }
+
+       throw new ObjectNotFoundException(name);
+    }
+
+    private static final Class[] proxyConstructorParamTypes
+       = new Class[] { ObjectImporter.class, int.class };
+
+    private Object createProxy(int oid, String classname) throws Exception {
+       Class c = Class.forName(classname);
+       Constructor cons = c.getConstructor(proxyConstructorParamTypes);
+       return cons.newInstance(new Object[] { this, new Integer(oid) });
+    }
+
+    /**
+     * Calls a method on a remote object.
+     * It sends a POST request to the server (via an http proxy server
+     * if needed).
+     *
+     * <p>This method is called by only proxy objects.
+     */
+    public Object call(int objectid, int methodid, Object[] args)
+       throws RemoteException
+    {
+       boolean result;
+       Object rvalue;
+       String errmsg;
+
+       try {
+           /* This method establishes a raw tcp connection for sending
+            * a POST message.  Thus the object cannot communicate a
+            * remote object beyond a fire wall.  To avoid this problem,
+            * the connection should be established with a mechanism
+            * collaborating a proxy server.  Unfortunately, java.lang.URL
+            * does not seem to provide such a mechanism.
+            *
+            * You might think that using HttpURLConnection is a better
+            * way than constructing a raw tcp connection.  Unfortunately,
+            * URL.openConnection() does not return an HttpURLConnection
+            * object in Netscape's JVM.  It returns a
+            * netscape.net.URLConnection object.
+            *
+            * lookupObject() has the same problem.
+            */
+           Socket sock = new Socket(servername, port);
+           OutputStream out = new BufferedOutputStream(
+                                               sock.getOutputStream());
+           out.write(rmiCommand);
+           out.write(endofline);
+           out.write(endofline);
+
+           ObjectOutputStream dout = new ObjectOutputStream(out);
+           dout.writeInt(objectid);
+           dout.writeInt(methodid);
+           writeParameters(dout, args);
+           dout.flush();
+
+           InputStream ins = new BufferedInputStream(sock.getInputStream());
+           skipHeader(ins);
+           ObjectInputStream din = new ObjectInputStream(ins);
+           result = din.readBoolean();
+           rvalue = null;
+           errmsg = null;
+           if (result)
+               rvalue = din.readObject();
+           else
+               errmsg = din.readUTF();
+
+           din.close();
+           dout.close();
+           sock.close();
+
+           if (rvalue instanceof RemoteRef) {
+               RemoteRef ref = (RemoteRef)rvalue;
+               rvalue = createProxy(ref.oid, ref.classname);
+           }
+       }
+       catch (ClassNotFoundException e) {
+           throw new RemoteException(e);
+       }
+       catch (IOException e) {
+           throw new RemoteException(e);
+       }
+       catch (Exception e) {
+           throw new RemoteException(e);
+       }
+
+       if (result)
+           return rvalue;
+       else
+           throw new RemoteException(errmsg);
+    }
+
+    private void skipHeader(InputStream in) throws IOException {
+       int len;
+       do {
+           int c;
+           len = 0;
+           while ((c = in.read()) >= 0 && c != 0x0d)
+               ++len;
+
+           in.read();  /* skip 0x0a (LF) */
+       } while (len > 0);
+    }
+
+    private void writeParameters(ObjectOutputStream dout, Object[] params)
+       throws IOException
+    {
+       int n = params.length;
+       dout.writeInt(n);
+       for (int i = 0; i < n; ++i)
+           if (params[i] instanceof Proxy) {
+               Proxy p = (Proxy)params[i];
+               dout.writeObject(new RemoteRef(p._getObjectId()));
+           }
+           else
+               dout.writeObject(params[i]);
+    }
+}
diff --git a/src/main/javassist/rmi/ObjectNotFoundException.java b/src/main/javassist/rmi/ObjectNotFoundException.java
new file mode 100644 (file)
index 0000000..e31db37
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.rmi;
+
+public class ObjectNotFoundException extends Exception {
+    public ObjectNotFoundException(String name) {
+       super(name + " is not exported");
+    }
+
+    public ObjectNotFoundException(String name, Exception e) {
+       super(name + " because of " + e.toString());
+    }
+}
diff --git a/src/main/javassist/rmi/Proxy.java b/src/main/javassist/rmi/Proxy.java
new file mode 100644 (file)
index 0000000..f310d35
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.rmi;
+
+/**
+ * An interface implemented by proxy classes.
+ *
+ * @see javassist.rmi.StubGenerator
+ */
+public interface Proxy {
+    int _getObjectId();
+}
diff --git a/src/main/javassist/rmi/RemoteException.java b/src/main/javassist/rmi/RemoteException.java
new file mode 100644 (file)
index 0000000..6b89c64
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.rmi;
+
+/**
+ * <code>RemoteException</code> represents any exception thrown
+ * during remote method invocation.
+ */
+public class RemoteException extends RuntimeException {
+    public RemoteException(String msg) {
+       super(msg);
+    }
+
+    public RemoteException(Exception e) {
+       super("by " + e.toString());
+    }
+}
diff --git a/src/main/javassist/rmi/RemoteRef.java b/src/main/javassist/rmi/RemoteRef.java
new file mode 100644 (file)
index 0000000..0017ecc
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.rmi;
+
+/**
+ * Remote reference.  This class is internally used for sending a remote
+ * reference through a network stream.
+ */
+public class RemoteRef implements java.io.Serializable {
+    public int oid;
+    public String classname;
+
+    public RemoteRef(int i) {
+       oid = i;
+       classname = null;
+    }
+
+    public RemoteRef(int i, String name) {
+       oid = i;
+       classname = name;
+    }
+}
diff --git a/src/main/javassist/rmi/Sample.java b/src/main/javassist/rmi/Sample.java
new file mode 100644 (file)
index 0000000..fee85d2
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.rmi;
+
+/**
+ * A template used for defining a proxy class.
+ * The class file of this class is read by the <code>StubGenerator</code>
+ * class.
+ */
+public class Sample {
+    private ObjectImporter importer;
+    private int objectId;
+
+    public Object forward(Object[] args, int identifier) {
+       return importer.call(objectId, identifier, args);
+    }
+
+    public static Object forwardStatic(Object[] args, int identifier)
+       throws RemoteException
+    {
+       throw new RemoteException("cannot call a static method.");
+    }
+}
diff --git a/src/main/javassist/rmi/StubGenerator.java b/src/main/javassist/rmi/StubGenerator.java
new file mode 100644 (file)
index 0000000..9e77c38
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.rmi;
+
+import java.io.*;
+import javassist.*;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import javassist.CtMethod.ConstParameter;
+
+/**
+ * A stub-code generator.  It is used for producing a proxy class.
+ *
+ * <p>The proxy class for class A is as follows:
+ *
+ * <ul><pre>public class A implements Proxy, Serializable {
+ *   private ObjectImporter importer;
+ *   private int objectId;
+ *   public int _getObjectId() { return objectId; }
+ *   public A(ObjectImporter oi, int id) {
+ *     importer = oi; objectId = id;
+ *   }
+ *
+ *   ... the same methods that the original class A declares ...
+ * }</pre></ul>
+ *
+ * <p>Instances of the proxy class is created by an
+ * <code>ObjectImporter</code> object.
+ */
+public class StubGenerator implements Translator {
+    private static final String fieldImporter = "importer";
+    private static final String fieldObjectId = "objectId";
+    private static final String accessorObjectId = "_getObjectId";
+    private static final String sampleClass = "javassist.rmi.Sample";
+
+    private ClassPool classPool;
+    private Hashtable proxyClasses;
+    private CtMethod forwardMethod;
+    private CtMethod forwardStaticMethod;
+
+    private CtClass[] proxyConstructorParamTypes;
+    private CtClass[] interfacesForProxy;
+    private CtClass[] exceptionForProxy;
+
+    /**
+     * Constructs a stub-code generator.
+     */
+    public StubGenerator() {
+       proxyClasses = new Hashtable();
+    }
+
+    /**
+     * Is a method declared in javassist.Translator.
+     *
+     * @see javassist.Translator#start(ClassPool)
+     */
+    public void start(ClassPool pool) throws NotFoundException {
+       classPool = pool;
+       CtClass c = pool.get(sampleClass);
+       forwardMethod = c.getDeclaredMethod("forward");
+       forwardStaticMethod = c.getDeclaredMethod("forwardStatic");
+
+       proxyConstructorParamTypes
+           = pool.get(new String[] { "javassist.rmi.ObjectImporter",
+                                        "int" });
+       interfacesForProxy
+           = pool.get(new String[] { "java.io.Serializable",
+                                        "javassist.rmi.Proxy" });
+       exceptionForProxy
+           = new CtClass[] { pool.get("javassist.rmi.RemoteException") };
+    }
+
+    public void onWrite(ClassPool pool, String classname) {}
+
+    /**
+     * Returns <code>true</code> if the specified class is a proxy class
+     * recorded by <code>makeProxyClass()</code>.
+     *
+     * @param name             a fully-qualified class name
+     */
+    public boolean isProxyClass(String name) {
+       return proxyClasses.get(name) != null;
+    }
+
+    /**
+     * Makes a proxy class.  The produced class is substituted
+     * for the original class.
+     *
+     * @param clazz            the class referenced
+     *                         through the proxy class.
+     * @return         <code>false</code> if the proxy class
+     *                 has been already produced.
+     */
+    public synchronized boolean makeProxyClass(Class clazz)
+       throws CannotCompileException, NotFoundException
+    {
+       String classname = clazz.getName();
+       if (proxyClasses.get(classname) != null)
+           return false;
+       else {
+           CtClass ctclazz = produceProxyClass(classPool.get(classname),
+                                               clazz);
+           proxyClasses.put(classname, ctclazz);
+           modifySuperclass(ctclazz);
+           return true;
+       }
+    }
+
+    private CtClass produceProxyClass(CtClass orgclass, Class orgRtClass)
+       throws CannotCompileException, NotFoundException
+    {
+       int modify = orgclass.getModifiers();
+       if (Modifier.isAbstract(modify) || Modifier.isNative(modify)
+           || !Modifier.isPublic(modify))
+           throw new CannotCompileException(orgclass.getName()
+                       + " must be public, non-native, and non-abstract.");
+
+       CtClass proxy = classPool.makeClass(orgclass.getName(),
+                                             orgclass.getSuperclass());
+
+       proxy.setInterfaces(interfacesForProxy);
+
+       CtField f
+           = new CtField(classPool.get("javassist.rmi.ObjectImporter"),
+                         fieldImporter, proxy);
+       f.setModifiers(Modifier.PRIVATE);
+       proxy.addField(f, CtField.Initializer.byParameter(0));
+
+       f = new CtField(CtClass.intType, fieldObjectId, proxy);
+       f.setModifiers(Modifier.PRIVATE);
+       proxy.addField(f, CtField.Initializer.byParameter(1)); 
+
+       proxy.addMethod(CtNewMethod.getter(accessorObjectId, f));
+
+       proxy.addConstructor(CtNewConstructor.defaultConstructor(proxy));
+       CtConstructor cons
+           = CtNewConstructor.skeleton(proxyConstructorParamTypes,
+                                       null, proxy);
+       proxy.addConstructor(cons);
+
+       try {
+           addMethods(proxy, orgRtClass.getMethods());
+           return proxy;
+       }
+       catch (SecurityException e) {
+           throw new CannotCompileException(e);
+       }
+    }
+
+    private CtClass toCtClass(Class rtclass) throws NotFoundException {
+       String name;
+       if (!rtclass.isArray())
+           name = rtclass.getName();
+       else {
+           StringBuffer sbuf = new StringBuffer();
+           do {
+               sbuf.append("[]");
+               rtclass = rtclass.getComponentType();
+           } while(rtclass.isArray());
+           sbuf.insert(0, rtclass.getName());
+           name = sbuf.toString();
+       }
+           
+       return classPool.get(name);
+    }
+
+    private CtClass[] toCtClass(Class[] rtclasses) throws NotFoundException {
+       int n = rtclasses.length;
+       CtClass[] ctclasses = new CtClass[n];
+       for (int i = 0; i < n; ++i)
+           ctclasses[i] = toCtClass(rtclasses[i]);
+
+       return ctclasses;
+    }
+
+    /* ms must not be an array of CtMethod.  To invoke a method ms[i]
+     * on a server, a client must send i to the server.
+     */
+    private void addMethods(CtClass proxy, Method[] ms)
+       throws CannotCompileException, NotFoundException
+    {
+       CtMethod wmethod;
+       for (int i = 0; i < ms.length; ++i) {
+           Method m = ms[i];
+           int mod = m.getModifiers();
+           if (m.getDeclaringClass() != Object.class
+                       && !Modifier.isFinal(mod))
+               if (Modifier.isPublic(mod)) {
+                   CtMethod body;
+                   if (Modifier.isStatic(mod))
+                       body = forwardStaticMethod;
+                   else
+                       body = forwardMethod;
+
+                   wmethod
+                       = CtNewMethod.wrapped(toCtClass(m.getReturnType()),
+                                             m.getName(),
+                                             toCtClass(m.getParameterTypes()),
+                                             exceptionForProxy,
+                                             body,
+                                             ConstParameter.integer(i),
+                                             proxy);
+                   wmethod.setModifiers(mod);
+                   proxy.addMethod(wmethod);
+               }
+               else if (!Modifier.isProtected(mod)
+                        && !Modifier.isPrivate(mod))
+                   // if package method
+                   throw new CannotCompileException(
+                       "the methods must be public, protected, or private.");
+       }
+    }
+
+    /**
+     * Adds a default constructor to the super classes.
+     */
+    private void modifySuperclass(CtClass orgclass)
+       throws CannotCompileException, NotFoundException
+    {
+       CtClass superclazz;
+       for (;; orgclass = superclazz) {
+           superclazz = orgclass.getSuperclass();
+           if (superclazz == null)
+               break;
+
+           String name = superclazz.getName();
+           try {
+               superclazz.getDeclaredConstructor(null);
+               break;  // the constructor with no arguments is found.
+           }
+           catch (NotFoundException e) {
+           }
+
+           superclazz.addConstructor(
+                       CtNewConstructor.defaultConstructor(superclazz));
+       }
+    }
+}
diff --git a/src/main/javassist/runtime/Cflow.java b/src/main/javassist/runtime/Cflow.java
new file mode 100644 (file)
index 0000000..693da24
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.runtime;
+
+/**
+ * A support class for implementing <code>$cflow</code>.
+ * This support class is required at runtime
+ * only if <code>$cflow</code> is used.
+ *
+ * @see javassist.CtBehavior#useCflow(String)
+ */
+public class Cflow extends ThreadLocal {
+    private static class Depth {
+       private int depth;
+       Depth() { depth = 0; }
+       int get() { return depth; }
+       void inc() { ++depth; }
+       void dec() { --depth; }
+    }
+
+    protected synchronized Object initialValue() {
+       return new Depth();
+    }
+
+    /**
+     * Increments the counter.
+     */
+    public void enter() { ((Depth)get()).inc(); }
+
+    /**
+     * Decrements the counter.
+     */
+    public void exit() { ((Depth)get()).dec(); }
+
+    /**
+     * Returns the value of the counter.
+     */
+    public int value() { return ((Depth)get()).get(); }
+}
diff --git a/src/main/javassist/runtime/Desc.java b/src/main/javassist/runtime/Desc.java
new file mode 100644 (file)
index 0000000..0bf49bf
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.runtime;
+
+/**
+ * A support class for implementing <code>$sig</code> and
+ * <code>$type</code>.
+ * This support class is required at runtime
+ * only if <code>$sig</code> or <code>$type</code> is used.
+ */
+public class Desc {
+
+    /**
+     * Specifies how a <code>java.lang.Class</code> object is loaded.
+     *
+     * <p>If true, it is loaded by:
+     * <ul><pre>Thread.currentThread().getContextClassLoader().loadClass()</pre></ul>
+     * <p>If false, it is loaded by <code>Class.forName()</code>.
+     * The default value is false.
+     */
+    public static boolean useContextClassLoader = false;
+
+    private static Class getClassObject(String name)
+       throws ClassNotFoundException
+    {
+       if (useContextClassLoader)
+           return Thread.currentThread().getContextClassLoader()
+                  .loadClass(name);
+       else
+           return Class.forName(name);
+    }
+
+    /**
+     * Interprets the given class name.
+     * It is used for implementing <code>$class</code>.
+     */
+    public static Class getClazz(String name) {
+       try {
+           return getClassObject(name);
+       }
+       catch (ClassNotFoundException e) {
+           throw new RuntimeException("$class: internal error");
+       }
+    }
+
+    /**
+     * Interprets the given type descriptor representing a method
+     * signature.  It is used for implementing <code>$sig</code>.
+     */
+    public static Class[] getParams(String desc) {
+       if (desc.charAt(0) != '(')
+           throw new RuntimeException("$sig: internal error");
+
+       return getType(desc, desc.length(), 1, 0);
+    }
+
+    /**
+     * Interprets the given type descriptor.
+     * It is used for implementing <code>$type</code>.
+     */
+    public static Class getType(String desc) {
+       Class[] result = getType(desc, desc.length(), 0, 0);
+       if (result == null || result.length != 1)
+           throw new RuntimeException("$type: internal error");
+
+       return result[0];
+    }
+
+    private static Class[] getType(String desc, int descLen,
+                                  int start, int num) {
+       Class clazz;
+       if (start >= descLen)
+           return new Class[num];
+
+       char c = desc.charAt(start);
+       switch (c) {
+       case 'Z' :
+           clazz = Boolean.TYPE;
+           break;
+       case 'C' :
+           clazz = Character.TYPE;
+           break;
+       case 'B' :
+           clazz = Byte.TYPE;
+           break;
+       case 'S' :
+           clazz = Short.TYPE;
+           break;
+       case 'I' :
+           clazz = Integer.TYPE;
+           break;
+       case 'J' :
+           clazz = Long.TYPE;
+           break;
+       case 'F' :
+           clazz = Float.TYPE;
+           break;
+       case 'D' :
+           clazz = Double.TYPE;
+           break;
+       case 'V' :
+           clazz = Void.TYPE;
+           break;
+       case 'L' :
+       case '[' :
+           return getClassType(desc, descLen, start, num);
+       default :
+           return new Class[num];
+       }
+
+       Class[] result = getType(desc, descLen, start + 1, num + 1);
+       result[num] = clazz;
+       return result;
+    }
+
+    private static Class[] getClassType(String desc, int descLen,
+                                       int start, int num) {
+       int end = start;
+       while (desc.charAt(end) == '[')
+           ++end;
+
+       if (desc.charAt(end) == 'L') {
+           end = desc.indexOf(';', end);
+           if (end < 0)
+               throw new IndexOutOfBoundsException("bad descriptor");
+       }
+
+       String cname;
+       if (desc.charAt(start) == 'L')
+           cname = desc.substring(start + 1, end);
+       else
+           cname = desc.substring(start, end + 1);
+
+       Class[] result = getType(desc, descLen, end + 1, num + 1);
+       try {
+           result[num] = getClassObject(cname.replace('/', '.'));
+       }
+       catch (ClassNotFoundException e) {
+           throw new RuntimeException(e);
+       }
+
+       return result;
+    }
+}
diff --git a/src/main/javassist/web/BadHttpRequest.java b/src/main/javassist/web/BadHttpRequest.java
new file mode 100644 (file)
index 0000000..aafebce
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.web;
+
+/**
+ * Thrown when receiving an invalid HTTP request.
+ */
+public class BadHttpRequest extends Exception {
+    private Exception e;
+
+    public BadHttpRequest() { e = null; }
+
+    public BadHttpRequest(Exception _e) { e = _e; }
+
+    public String toString() {
+       if (e == null)
+           return super.toString();
+       else
+           return e.toString();
+    }
+}
diff --git a/src/main/javassist/web/Viewer.java b/src/main/javassist/web/Viewer.java
new file mode 100644 (file)
index 0000000..ce57c01
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.web;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * An applet viewer.
+ *
+ * <p>This is a sort of applet viewer that can run any program even if
+ * the main class is not a subclass of <code>Applet</code>.
+ * This viewwer first calls <code>main()</code> in the main class.
+ *
+ * <p>To run, you should type:
+ *
+ * <ul><code>% java javassist.web.Viewer <i>host port</i> Main arg1, ...</code></ul>
+ *
+ * <p>This command calls <code>Main.main()</code> with <code>arg1,...</code>
+ * All classes including <code>Main</code> are fetched from
+ * a server http://<i>host</i>:<i>port</i>.
+ * Only the class file for <code>Viewer</code> must exist
+ * on a local file system at the client side; even other
+ * <code>javassist.*</code> classes are not needed at the client side.
+ * <code>Viewer</code> uses only Java core API classes.
+ *
+ * <p>Note: since a <code>Viewer</code> object is a class loader,
+ * a program loaded by this object can call a method in <code>Viewer</code>.
+ * For example, you can write something like this:
+ *
+ * <ul><pre>
+ * Viewer v = (Viewer)this.getClass().getClassLoader();
+ * String port = v.getPort();
+ * </pre></ul>
+ *
+ */
+public class Viewer extends ClassLoader {
+    private String server;
+    private int port;
+
+    /**
+     * Starts a program.
+     */
+    public static void main(String[] args) throws Throwable {
+       if (args.length >= 3) {
+           Viewer cl = new Viewer(args[0], Integer.parseInt(args[1]));
+           String[] args2 = new String[args.length - 3];
+           System.arraycopy(args, 3, args2, 0, args.length - 3);
+           cl.run(args[2], args2);
+       }
+       else
+           System.err.println(
+       "Usage: java javassist.web.Viewer <host> <port> class [args ...]");
+    }
+
+    /**
+     * Constructs a viewer.
+     *
+     * @param host             server name
+     * @param p                        port number
+     */
+    public Viewer(String host, int p) {
+       server = host;
+       port = p;
+    }
+
+    /**
+     * Returns the server name.
+     */
+    public String getServer() { return server; }
+
+    /**
+     * Returns the port number.
+     */
+    public int getPort() { return port; }
+
+    /**
+     * Invokes main() in the class specified by <code>classname</code>.
+     *
+     * @param classname                executed class
+     * @param args             the arguments passed to <code>main()</code>.
+     */
+    public void run(String classname, String[] args)
+       throws Throwable
+    {
+       Class c = loadClass(classname);
+       try {
+           c.getDeclaredMethod("main", new Class[] { String[].class })
+               .invoke(null, new Object[] { args });
+       }
+       catch (java.lang.reflect.InvocationTargetException e) {
+           throw e.getTargetException();
+       }
+    }
+
+    /**
+     * Requests the class loader to load a class.
+     */
+    protected synchronized Class loadClass(String name, boolean resolve)
+       throws ClassNotFoundException
+    {
+       Class c = findLoadedClass(name);
+       if (c == null)
+           c = findClass(name);
+
+       if (c == null)
+           throw new ClassNotFoundException(name);
+
+       if (resolve)
+           resolveClass(c);
+
+       return c;
+    }
+
+    /**
+     * Finds the specified class.  The implementation in this class
+     * fetches the class from the http server.  If the class is
+     * either <code>java.*</code>, <code>javax.*</code>, or
+     * <code>Viewer</code>, then it is loaded by the parent class
+     * loader.
+     *
+     * <p>This method can be overridden by a subclass of
+     * <code>Viewer</code>.
+     */
+    protected Class findClass(String name) throws ClassNotFoundException {
+       Class c = null;
+       if (name.startsWith("java.") || name.startsWith("javax.")
+           || name.equals("javassist.web.Viewer"))
+           c = findSystemClass(name);
+
+       if (c == null)
+           try {
+               byte[] b = fetchClass(name);
+               if (b != null)
+                   c = defineClass(name, b, 0, b.length);
+           }
+       catch (Exception e) {
+       }
+
+       return c;
+    }
+
+    /**
+     * Fetches the class file of the specified class from the http
+     * server.
+     */
+    protected byte[] fetchClass(String classname) throws Exception
+    {
+       byte[] b;
+       URL url = new URL("http", server, port,
+                         "/" + classname.replace('.', '/') + ".class");
+       URLConnection con = url.openConnection();
+       con.connect();
+       int size = con.getContentLength();
+       InputStream s = con.getInputStream();
+       if (size <= 0)
+           b = readStream(s);
+       else {
+           b = new byte[size];
+           int len = 0;
+           do {
+               int n = s.read(b, len, size - len);
+               if (n < 0) {
+                   s.close();
+                   throw new IOException("the stream was closed: "
+                                         + classname);
+               }
+               len += n;
+           } while (len < size);
+       }
+
+       s.close();
+       return b;
+    }
+
+    private byte[] readStream(InputStream fin) throws IOException {
+       byte[] buf = new byte[4096];
+       int size = 0;
+       int len = 0;
+       do {
+           size += len;
+           if (buf.length - size <= 0) {
+               byte[] newbuf = new byte[buf.length * 2];
+               System.arraycopy(buf, 0, newbuf, 0, size);
+               buf = newbuf;
+           }
+
+           len = fin.read(buf, size, buf.length - size);
+       } while (len >= 0);
+
+       byte[] result = new byte[size];
+       System.arraycopy(buf, 0, result, 0, size);
+       return result;
+    }
+}
diff --git a/src/main/javassist/web/Webserver.java b/src/main/javassist/web/Webserver.java
new file mode 100644 (file)
index 0000000..95ad05a
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * This file is part of the Javassist toolkit.
+ *
+ * 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.  You may obtain a copy of the License at
+ * either http://www.mozilla.org/MPL/.
+ *
+ * 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.
+ *
+ * The Original Code is Javassist.
+ *
+ * The Initial Developer of the Original Code is Shigeru Chiba.  Portions
+ * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+ * All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * The development of this software is supported in part by the PRESTO
+ * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation.
+ */
+
+package javassist.web;
+
+import java.net.*;
+import java.io.*;
+import java.util.Hashtable;
+import java.util.Date;
+import javassist.ClassPool;
+import javassist.Translator;
+
+/**
+ * A web server for Javassist.
+ *
+ * <p>This enables a Java program to instrument class files loaded by
+ * web browsers for applets.  Since the (standard) security manager
+ * does not allow an applet to create and use a class loader,
+ * instrumenting class files must be done by this web server.
+ *
+ * <p>Programmers can register a <code>ClassPool</code> object for
+ * instrumenting class files when they are sent to web browsers.
+ *
+ * <p><b>Note:</b> although this class is included in the Javassist API,
+ * it is provided as a sample implementation of the web server using
+ * Javassist.  Especially, there might be security flaws in this server.
+ * Please use this with YOUR OWN RISK.
+ */
+public class Webserver {
+    private ServerSocket socket;
+    private ClassPool classPool;
+
+    private final static byte[] endofline = { 0x0d, 0x0a };
+    private byte[] filebuffer = new byte[4096];
+
+    private final static int typeHtml = 1;
+    private final static int typeClass = 2;
+    private final static int typeGif = 3;
+    private final static int typeJpeg = 4;
+    private final static int typeText = 5;
+    private final static int typeUnknown = 6;
+
+    /**
+     * If this field is not null, the class files taken from
+     * <code>ClassPool</code> are written out under the directory
+     * specified by this field.  The directory name must not end
+     * with a directory separator.
+     */
+    public String debugDir = null;
+
+    /**
+     * The top directory of html (and .gif, .class, ...) files.
+     * It must end with the directory separator such as "/".
+     * (For portability, "/" should be used as the directory separator.
+     * Javassist automatically translates "/" into a platform-dependent
+     * character.)
+     * If this field is null, the top directory is the current one where
+     * the JVM is running.
+     *
+     * <p>If the given URL indicates a class file and the class file
+     * is not found under the directory specified by this variable,
+     * then <code>Class.getResourceAsStream()</code> is called
+     * for searching the Java class paths.
+     */
+    public String htmlfileBase = null;
+
+    /**
+     * Starts a web server.
+     * The port number is specified by the first argument.
+     */
+    public static void main(String[] args) throws IOException {
+       if (args.length == 1) {
+           Webserver web = new Webserver(args[0]);
+           web.run();
+       }
+       else
+           System.err.println(
+                       "Usage: java javassist.web.Webserver <port number>");
+    }
+
+    /**
+     * Constructs a web server.
+     *
+     * @param port     port number
+     */
+    public Webserver(String port) throws IOException {
+       this(Integer.parseInt(port));
+    }
+
+    /**
+     * Constructs a web server.
+     *
+     * @param port     port number
+     */
+    public Webserver(int port) throws IOException {
+       socket = new ServerSocket(port);
+       classPool = null;
+    }
+
+    /**
+     * Requests the web server to use the specified
+     * <code>ClassPool</code> object for obtaining a class file.
+     */
+    public void setClassPool(ClassPool loader) {
+       classPool = loader;
+    }
+
+    /**
+     * Closes the socket.
+     */
+    public void end() throws IOException {
+       socket.close();
+    }
+
+    /**
+     * Prints a log message.
+     */
+    public void logging(String msg) {
+       System.out.println(msg);
+    }
+
+    /**
+     * Prints a log message.
+     */
+    public void logging(String msg1, String msg2) {
+       System.out.print(msg1);
+       System.out.print(" ");
+       System.out.println(msg2);
+    }
+
+    /**
+     * Prints a log message.
+     */
+    public void logging(String msg1, String msg2, String msg3) {
+       System.out.print(msg1);
+       System.out.print(" ");
+       System.out.print(msg2);
+       System.out.print(" ");
+       System.out.println(msg3);
+    }
+
+    /**
+     * Prints a log message with indentation.
+     */
+    public void logging2(String msg) {
+       System.out.print("    ");
+       System.out.println(msg);
+    }
+
+    /**
+     * Begins the HTTP service.
+     */
+    public void run() {
+       System.err.println("ready to service...");
+       for (;;)
+           try {
+               ServiceThread th = new ServiceThread(this, socket.accept());
+               th.start();
+           }
+           catch (IOException e) {
+               logging(e.toString());
+           }
+    }
+
+    final void process(Socket clnt) throws IOException {
+       InputStream in = new BufferedInputStream(clnt.getInputStream());
+       String cmd = readLine(in);
+       logging(clnt.getInetAddress().getHostName(),
+               new Date().toString(), cmd);
+       while (skipLine(in) > 0){
+       }
+
+       OutputStream out = new BufferedOutputStream(clnt.getOutputStream());
+       try {
+           doReply(in, out, cmd);
+       }
+       catch (BadHttpRequest e) {
+           replyError(out, e);
+       }
+
+       out.flush();
+       in.close();
+       out.close();
+       clnt.close();
+    }
+
+    private String readLine(InputStream in) throws IOException {
+       StringBuffer buf = new StringBuffer();
+       int c;
+       while ((c = in.read()) >= 0 && c != 0x0d)
+           buf.append((char)c);
+
+       in.read();      /* skip 0x0a (LF) */
+       return buf.toString();
+    }
+
+    private int skipLine(InputStream in) throws IOException {
+       int c;
+       int len = 0;
+       while ((c = in.read()) >= 0 && c != 0x0d)
+           ++len;
+
+       in.read();      /* skip 0x0a (LF) */
+       return len;
+    }
+
+    /**
+     * Proceses a HTTP request from a client.
+     *
+     * @param out      the output stream to a client
+     * @param cmd      the command received from a client
+     */
+    public void doReply(InputStream in, OutputStream out, String cmd)
+       throws IOException, BadHttpRequest
+    {
+       int len;
+       int fileType;
+       String filename, urlName;
+
+       if (cmd.startsWith("GET /"))
+           filename = urlName = cmd.substring(5, cmd.indexOf(' ', 5));
+       else
+           throw new BadHttpRequest();
+
+       if (filename.endsWith(".class"))
+           fileType = typeClass;
+       else if (filename.endsWith(".html") || filename.endsWith(".htm"))
+           fileType = typeHtml;
+       else if (filename.endsWith(".gif"))
+           fileType = typeGif;
+       else if (filename.endsWith(".jpg"))
+           fileType = typeJpeg;
+       else
+           fileType = typeText;        // or textUnknown
+
+       len = filename.length();
+       if (fileType == typeClass
+           && letUsersSendClassfile(out, filename, len))
+           return;
+
+       checkFilename(filename, len);
+       if (htmlfileBase != null)
+           filename = htmlfileBase + filename;
+
+       if (File.separatorChar != '/')
+           filename = filename.replace('/', File.separatorChar);
+
+       File file = new File(filename);
+       if (file.canRead()) {
+           sendHeader(out, file.length(), fileType);
+           FileInputStream fin = new FileInputStream(file);
+           for (;;) {
+               len = fin.read(filebuffer);
+               if (len <= 0)
+                   break;
+               else
+                   out.write(filebuffer, 0, len);
+           }
+
+           fin.close();
+           return;
+       }
+
+       // If the file is not found under the html-file directory,
+       // then Class.getResourceAsStream() is tried.
+
+       if (fileType == typeClass) {
+           InputStream fin
+               = getClass().getResourceAsStream("/" + urlName);
+           if (fin != null) {
+               ByteArrayOutputStream barray = new ByteArrayOutputStream();
+               for (;;) {
+                   len = fin.read(filebuffer);
+                   if (len <= 0)
+                       break;
+                   else
+                       barray.write(filebuffer, 0, len);
+               }
+
+               byte[] classfile = barray.toByteArray();
+               sendHeader(out, classfile.length, typeClass);
+               out.write(classfile);
+               fin.close();
+               return;
+           }
+       }
+
+       throw new BadHttpRequest();
+    }
+
+    private void checkFilename(String filename, int len)
+       throws BadHttpRequest
+    {
+       for (int i = 0; i < len; ++i) {
+           char c = filename.charAt(i);
+           if (!Character.isJavaIdentifierPart(c) && c != '.' && c != '/')
+               throw new BadHttpRequest();
+       }
+
+       if (filename.indexOf("..") >= 0)
+           throw new BadHttpRequest();
+    }
+
+    private boolean letUsersSendClassfile(OutputStream out,
+                                         String filename, int length)
+       throws IOException, BadHttpRequest
+    {
+       if (classPool == null)
+           return false;
+
+       byte[] classfile;
+       String classname
+           = filename.substring(0, length - 6).replace('/', '.');
+       try {
+           classfile = classPool.write(classname);
+           if (debugDir != null)
+               classPool.writeFile(classname, debugDir);
+       }
+       catch (Exception e) {
+           throw new BadHttpRequest(e);
+       }
+
+       sendHeader(out, classfile.length, typeClass);
+       out.write(classfile);
+       return true;
+    }
+
+    private void sendHeader(OutputStream out, long dataLength, int filetype)
+       throws IOException
+    {
+       out.write("HTTP/1.0 200 OK".getBytes());
+       out.write(endofline);
+       out.write("Content-Length: ".getBytes());
+       out.write(Long.toString(dataLength).getBytes());
+       out.write(endofline);
+       if (filetype == typeClass)
+           out.write("Content-Type: application/octet-stream".getBytes());
+       else if (filetype == typeHtml)
+           out.write("Content-Type: text/html".getBytes());
+       else if (filetype == typeGif)
+           out.write("Content-Type: image/gif".getBytes());
+       else if (filetype == typeJpeg)
+           out.write("Content-Type: image/jpg".getBytes());
+       else if (filetype == typeText)
+           out.write("Content-Type: text/plain".getBytes());
+
+       out.write(endofline);
+       out.write(endofline);
+    }
+
+    private void replyError(OutputStream out, BadHttpRequest e)
+       throws IOException
+    {
+       logging2("bad request: " + e.toString());
+       out.write("HTTP/1.0 400 Bad Request".getBytes());
+       out.write(endofline);
+       out.write(endofline);
+       out.write("<H1>Bad Request</H1>".getBytes());
+    }
+}
+
+class ServiceThread extends Thread {
+    Webserver web;
+    Socket sock;
+
+    public ServiceThread(Webserver w, Socket s) {
+       web = w;
+       sock = s;
+    }
+
+    public void run() {
+       try {
+           web.process(sock);
+       }
+       catch (IOException e) {
+       }
+    }
+}
diff --git a/tutorial/brown.css b/tutorial/brown.css
new file mode 100644 (file)
index 0000000..b88ddb4
--- /dev/null
@@ -0,0 +1,19 @@
+h1,h2,h3 {\r
+       color:#663300;\r
+       padding:4px 6px 6px 10px;\r
+       border-width:1px 0px 1px 0px;\r
+       border-color:#F5DEB3;\r
+       border-style:solid;\r
+}\r
+\r
+h3 {\r
+       padding-left: 30px;\r
+}\r
+\r
+h4 {\r
+       color:#663300;\r
+}\r
+\r
+em {\r
+       color:#cc0000;\r
+}\r
diff --git a/tutorial/overview.gif b/tutorial/overview.gif
new file mode 100644 (file)
index 0000000..953c59a
Binary files /dev/null and b/tutorial/overview.gif differ
diff --git a/tutorial/sequence.gif b/tutorial/sequence.gif
new file mode 100644 (file)
index 0000000..a59d66f
Binary files /dev/null and b/tutorial/sequence.gif differ
diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html
new file mode 100644 (file)
index 0000000..3fac03e
--- /dev/null
@@ -0,0 +1,560 @@
+<html>\r
+<head>\r
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\r
+   <title>Javassist Tutorial</title>\r
+   <link rel="stylesheet" type="text/css" href="brown.css">\r
+</head>\r
+<body>\r
+\r
+<b>\r
+<font size="+3">\r
+Getting Started with Javassist\r
+</font>\r
+\r
+<p><font size="+2">\r
+Shigeru Chiba\r
+</font>\r
+</b>\r
+\r
+<p><div align="right"><a href="tutorial2.html">Next page</a></div>\r
+\r
+<ul>1. <a href="#read">Reading bytecode</a>\r
+<br>2. <a href="#def">Defining a new class</a>\r
+<br>3. <a href="#mod">Modifying a class at load time</a>\r
+<br>4. <a href="#load">Class loader</a>\r
+<br>5. <a href="tutorial2.html#intro">Introspection and customization</a>\r
+</ul>\r
+\r
+<p><br>\r
+\r
+<a name="read">\r
+<h2>1. Reading bytecode</h2>\r
+\r
+<p>Javassist is a class library for dealing with Java bytecode.\r
+Java bytecode is stored in a binary file called a class file.\r
+Each class file contains one Java class or interface.\r
+\r
+<p>The class <code>Javassist.CtClass</code> is an abstract representation\r
+of a class file.  A <code>CtClass</code> object is a handle for dealing\r
+with a class file.  The following program is a very simple example:\r
+\r
+<ul><pre>\r
+ClassPool pool = ClassPool.getDefault();\r
+CtClass cc = pool.get("test.Rectangle");\r
+cc.setSuperclass(pool.get("test.Point"));\r
+pool.writeFile("test.Rectangle");          // or simply, cc.writeFile()\r
+</pre></ul>\r
+\r
+<p>This program first obtains a <code>ClassPool</code> object,\r
+which controls bytecode modification with Javassist.\r
+The <code>ClassPool</code> object is a container of <code>CtClass</code>\r
+object representing a class file.\r
+It reads a class file on demand for constructing a <code>CtClass</code>\r
+object and contains the constructed object until it is written out\r
+to a file or an output stream.\r
+\r
+<p>To modify the definition of a class, the users must first obtain a\r
+reference to the <code>CtClass</code> object representing that class.\r
+<code>ClassPool.get()</code> is used for this purpose.\r
+In the case of the program above, the <code>CtClass</code> object\r
+representing a class <code>test.Rectangle</code> is obtained from\r
+the <code>ClassPool</code> object\r
+and it is assigned\r
+to a variable <code>cc</code>.  Then it is modified so that\r
+the superclass of <code>test.Rectangle</code> is changed into\r
+a class <code>test.Point</code>.\r
+This change is reflected on the original class file when\r
+<code>ClassPool.writeFile()</code> is finally called.\r
+\r
+<p>Note that <code>writeFile()</code> is a method declared in not\r
+<code>CtClass</code> but <code>ClassPool</code>.\r
+If this method is called, the <code>ClassPool</code>\r
+finds a <code>CtClass</code> object specified with a class name\r
+among the objects that the <code>ClassPool</code> contains.\r
+Then it translates that <code>CtClass</code> object into a class file\r
+and writes it on a local disk.\r
+\r
+<p>There is also <code>writeFile()</code> defined in <code>CtClass</code>.\r
+Thus, the last line in the program above can be rewritten into:\r
+\r
+<ul><pre>cc.writeFile();</pre></ul>\r
+\r
+<p>This method is a convenient method for invoking <code>writeFile()</code>\r
+in <code>ClassPool</code> with the name of the class represented by\r
+<code>cc</code>.\r
+\r
+<p>Javassist also provides a method for directly obtaining the\r
+modified bytecode.  To do this, call <code>write()</code>:\r
+\r
+<ul><pre>\r
+byte[] b = pool.write("test.Rectangle");\r
+</pre></ul>\r
+\r
+<p>The contents of the class file for <code>test.Rectangle</code> are\r
+assigned to a variable <code>b</code> in the form of byte array.\r
+<code>writeFile()</code> also internally calls <code>write()</code>\r
+to obtain the byte array written in a class file.\r
+\r
+<p>The default <code>ClassPool</code> returned\r
+by a static method <code>ClassPool.getDefault()</code>\r
+searches the same path as the underlying JVM.\r
+The users can expand this class search path if needed.\r
+For example, the following code adds a directory\r
+<code>/usr/local/javalib</code>\r
+to the search path:\r
+\r
+<ul><pre>\r
+ClassPool pool = ClassPool.getDefault();\r
+pool.insertClassPath("/usr/local/javalib");\r
+</pre></ul>\r
+\r
+<p>The search path that the users can add is not only a directory but also\r
+a URL:\r
+\r
+<ul><pre>\r
+ClassPool pool = ClassPool.getDefault();\r
+ClassPath cp = new URLClassPath("www.foo.com", 80, "/java/", "com.foo.");\r
+pool.insertClassPath(cp);\r
+</pre></ul>\r
+\r
+<p>This program adds "http://www.foo.com:80/java/" to the class search\r
+path.  This URL is used only for searching classes belonging to a\r
+package <code>com.foo</code>.\r
+\r
+<p>You can directly give a byte array to a <code>ClassPool</code> object\r
+and construct a <code>CtClass</code> object from that array.  To do this,\r
+use <code>ByteArrayClassPath</code>.  For example,\r
+\r
+<ul><pre>\r
+ClassPool cp = ClassPool.getDefault();\r
+byte[] b = <em>a byte array</em>;\r
+String name = <em>class name</em>;\r
+cp.insertClassPath(new ByteArrayClassPath(name, b));\r
+CtClass cc = cp.get(name);\r
+</pre></ul>\r
+\r
+<p>The obtained <code>CtClass</code> object represents\r
+a class defined by the class file specified by <code>b</code>.\r
+\r
+\r
+<p>Since <code>ClassPath</code> is an interface, the users can define\r
+a new class implementing this interface and they can add an instance\r
+of that class so that a class file is obtained from a non-standard resource.\r
+\r
+<p><br>\r
+\r
+<a name="def">\r
+<h2>2. Defining a new class</h2>\r
+\r
+<p>To define a new class from scratch, <code>makeClass()</code>\r
+must be called on a <code>ClassPool</code>.\r
+\r
+<ul><pre>\r
+ClassPool pool = ClassPool.getDefault();\r
+CtClass cc = pool.makeClass("Point");\r
+</pre></ul>\r
+\r
+<p>This program defines a class <code>Point</code>\r
+including no members.\r
+\r
+<p>A new class can be also defined as a copy of an existing class.\r
+The program below does that:\r
+\r
+<ul><pre>\r
+ClassPool pool = ClassPool.getDefault();\r
+CtClass cc = pool.makeClass("Point");\r
+cc.setName("Pair");\r
+</pre></ul>\r
+\r
+<p>This program first obtains the <code>CtClass</code> object\r
+for class <code>Point</code>.  Then it gives a new name <code>Pair</code>\r
+to that <code>CtClass</code> object.\r
+If <code>get("Point")</code> is called on the <code>ClassPool</code>\r
+object, then a class file <code>Point.class</code> is read again and\r
+a new <code>CtClass</code> object for class <code>Point</code> is constructed\r
+again.\r
+\r
+<ul><pre>\r
+ClassPool pool = ClassPool.getDefault();\r
+CtClass cc = pool.makeClass("Point");\r
+CtClass cc1 = pool.get("Point");   // cc1 is identical to cc.\r
+cc.setName("Pair");\r
+CtClass cc2 = pool.get("Pair");    // cc2 is identical to cc.\r
+CtClass cc3 = pool.get("Point");   // cc3 is not identical to cc.\r
+</pre></ul>\r
+\r
+<p><br>\r
+\r
+<a name="mod">\r
+<h2>3. Modifying a class at load time</h2>\r
+\r
+<p>If what classes are modified is known in advance,\r
+the easiest way for modifying the classes is as follows:\r
+\r
+<ul><li>1. Get a <code>CtClass</code> object by calling\r
+        <code>ClassPool.get()</code>,\r
+    <li>2. Modify it, and\r
+    <li>3. Call <code>ClassPool.write()</code> or <code>writeFile()</code>.\r
+</ul>\r
+\r
+<p>If whether a class is modified or not is determined at load time,\r
+the users can write an event listener so that it is notified\r
+when a class is loaded into the JVM.\r
+A class loader (<code>java.lang.ClassLoader</code>) working with\r
+Javassist must call <code>ClassPool.write()</code> for obtaining\r
+a class file.  The users can write an event listener so that it is\r
+notified when the class loader calls <code>ClassPool.write()</code>.\r
+The event-listener class must implement the following interface:\r
+\r
+<ul><pre>public interface Translator {\r
+    public void start(ClassPool pool)\r
+        throws NotFoundException, CannotCompileException;\r
+    public void onWrite(ClassPool pool, String classname)\r
+        throws NotFoundException, CannotCompileException;\r
+}</pre></ul>\r
+\r
+<p>The method <code>start()</code> is called when this event listener\r
+is registered to a <code>ClassPool</code> object.\r
+The method <code>onWrite()</code> is called when <code>write()</code>\r
+(or similar methods) is called on the <code>ClassPool</code> object.\r
+The second parameter of <code>onWrite()</code> is the name of the class\r
+to be written out.\r
+\r
+<p>Note that <code>start()</code> or <code>onWrite()</code> do not have\r
+to call <code>write()</code> or <code>writeFile()</code>.  For example,\r
+\r
+<ul><pre>public class MyAnotherTranslator implements Translator {\r
+    public void start(ClassPool pool)\r
+        throws NotFoundException, CannotCompileException {}\r
+    public void onWrite(ClassPool pool, String classname)\r
+        throws NotFoundException, CannotCompileException\r
+    {\r
+        CtClass cc = pool.get(classname);\r
+        cc.setModifiers(Modifier.PUBLIC);\r
+    }\r
+}</pre></ul>\r
+\r
+<p>All the classes written out by <code>write()</code> are made public\r
+just before their definitions are translated into an byte array.\r
+\r
+<p><center><img src="overview.gif" alt="overview"></center>\r
+\r
+<p>The two methods <code>start()</code> and <code>onWrite()</code>\r
+can modify not only a <code>CtClass</code> object specified by\r
+the given <code>classname</code> but also\r
+<em>any</em> <code>CtClass</code> objects contained\r
+in the given <code>ClassPool</code>.  \r
+They can call <code>ClassPool.get()</code> for obtaining any\r
+<code>CtClass</code> object.\r
+If a modified <code>CtClass</code> object is not written out immediately,\r
+the modification is recorded until that object is written out.\r
+\r
+<p><center><img src="sequence.gif" alt="sequence diagram"></center>\r
+\r
+<p>To register an event listener to a <code>ClassPool</code>,\r
+it must be passed to a constructor of <code>ClassPool</code>.  \r
+Only a single event listener can be registered.\r
+If more than one event listeners are needed, multiple\r
+<code>ClassPool</code>s should be connected to be a single\r
+stream.  For example,\r
+\r
+<ul><pre>Translator t1 = new MyTranslator();\r
+ClassPool c1 = new ClassPool(t1);\r
+Translator t2 = new MyAnotherTranslator();\r
+ClassPool c2 = new ClassPool(c1, t2);</pre></ul>\r
+\r
+<p>This program connects two <code>ClassPool</code>s.\r
+If a class loader calls <code>write()</code> on <code>c2</code>,\r
+the specified class file is first modified by <code>t1</code> and\r
+then by <code>t2</code>.  <code>write()</code> returns the resulting\r
+class file.\r
+\r
+First, <code>onWrite()</code> on <code>t1</code> is called since\r
+<code>c2</code> obtains a class file by calling <code>write()</code>\r
+on <code>c1</code>.  Then <code>onWrite()</code> on <code>t2</code>\r
+is called.  If <code>onWrite()</code> called on <code>t2</code>\r
+obtains a <code>CtClass</code> object from <code>c2</code>, that\r
+<code>CtClass</code> object represents the class file that\r
+<code>t1</code> has modified.\r
+\r
+<p><center><img src="two.gif" alt="two translators"></center>\r
+\r
+<p><br>\r
+\r
+<a name="load">\r
+<h2>4. Class loader</h2>\r
+\r
+<p>Javassist can be used with a class loader so that bytecode can be\r
+modified at load time.  The users of Javassist can define their own\r
+version of class loader but they can also use a class loader provided\r
+by Javassist.\r
+\r
+<p><br>\r
+\r
+<h3>4.1 Using <code>javassist.Loader</code></h3>\r
+\r
+<p>Javassist provides a class loader\r
+<code>javassist.Loader</code>.  This class loader uses a\r
+<code>javassist.ClassPool</code> object for reading a class file.\r
+\r
+<p>For example, <code>javassist.Loader</code> can be used for loading\r
+a particular class modified with Javassist.\r
+\r
+<ul><pre>\r
+import javassist.*;\r
+import test.Rectangle;\r
+\r
+public class Main {\r
+  public static void main(String[] args) throws Throwable {\r
+     ClassPool pool = ClassPool.getDefault();\r
+     Loader cl = new Loader(pool);\r
+\r
+     CtClass ct = pool.get("test.Rectangle");\r
+     ct.setSuperclass(pool.get("test.Point"));\r
+\r
+     Class c = cl.loadClass("test.Rectangle");\r
+     Object rect = c.newInstance();\r
+         :\r
+  }\r
+}\r
+</pre></ul>\r
+\r
+<p>This program modifies a class <code>test.Rectangle</code>.  The\r
+superclass of <code>test.Rectangle</code> is set to a\r
+<code>test.Point</code> class.  Then this program loads the modified\r
+class into the JVM, and creates a new instance of the\r
+<code>test.Rectangle</code> class.\r
+\r
+<p>The users can use a <code>javassist.Translator</code> object\r
+for modifying class files.\r
+Suppose that an instance of a class <code>MyTranslator</code>,\r
+which implements\r
+<code>javassist.Translator</code>, performs modification of class files.\r
+To run an application class <code>MyApp</code> with the\r
+<code>MyTranslator</code> object, write a main class:\r
+\r
+<ul><pre>\r
+import javassist.*;\r
+\r
+public class Main2 {\r
+  public static void main(String[] args) throws Throwable {\r
+     Translator t = new MyTranslator();\r
+     ClassPool pool = ClassPool.getDefault(t);\r
+     Loader cl = new Loader(pool);\r
+     cl.run("MyApp", args);\r
+  }\r
+}\r
+</pre></ul>\r
+\r
+<p>To run this program, do:\r
+\r
+<ul><pre>\r
+% java Main <i>arg1</i> <i>arg2</i>...\r
+</pre></ul>\r
+\r
+<p>The class <code>MyApp</code> and the other application classes\r
+are translated by <code>MyTranslator</code>.\r
+\r
+<p>Note that <em>application</em> classes like <code>MyApp</code> cannot\r
+access the <em>loader</em> classes such as <code>Main</code>,\r
+<code>MyTranslator</code> and <code>ClassPool</code> because they\r
+are loaded by different loaders.  The application classes are loaded\r
+by <code>javassist.Loader</code> whereas the loader classes such as\r
+<code>Main</code> are by the default Java class loader.\r
+\r
+<p>In Java, for security reasons, a single class file may be loaded\r
+into the JVM by two distinct class loaders so that two different\r
+classes would be created.  For example,\r
+\r
+<ul><pre>class Point {\r
+    int x, y;\r
+}\r
+\r
+class Box {\r
+    Point base;\r
+    Point getBase() { return base; }\r
+}\r
+\r
+class Window {\r
+    Point size;\r
+    Point getSize() { return size; }\r
+}</pre></ul>\r
+\r
+<p>Suppose that a class <code>Box</code> is loaded by a class loader\r
+<code>L1</code> while a class <code>Window</code> is loaded by a class\r
+loader <code>L2</code>.  Then, the obejcts returned by\r
+<code>getBase()</code> and <code>getSize()</code> are not instances of\r
+the same class <code>Point</code>.\r
+<code>getBase()</code> returns an instance of the class <code>Point</code>\r
+loaded by <code>L1</code> whereas <code>getSize()</code> returns an\r
+instance of <code>Point</code> loaded by <code>L2</code>.  The two versions\r
+of the class <code>Point</code> are distinct.  They belong to different\r
+name spaces.  For more details, see the following paper:\r
+\r
+<ul>Sheng Liang and Gilad Bracha,\r
+"Dynamic Class Loading in the Java Virtual Machine",\r
+<br><i>ACM OOPSLA'98</i>, pp.36-44, 1998.</ul>\r
+\r
+<p>To avoid this problem, the two class loaders <code>L1</code> and\r
+<code>L2</code> must delegate the loading operation of the class\r
+<code>Point</code> to another class loader, <code>L3</code>, which is\r
+a parent class loader of <code>L1</code> and <code>L2</code>.\r
+<code>delegateLoadingOf()</code> in <code>javassist.Loader</code>\r
+is a method for specifying what classes should be loaded by the\r
+parent loader.\r
+\r
+<p>If <code>L1</code> is the parent class loader of <code>L2</code>,\r
+that is, if <code>L1</code> loads the class of <code>L2</code>,\r
+then <code>L2</code> can delegate the loading operation of\r
+<code>Point</code> to <code>L1</code> for avoiding the problem above.\r
+However, this technique does not work in the case below:\r
+\r
+<ul><pre>class Point {     // loaded by L1\r
+    Window win;\r
+    int x, y;\r
+}\r
+\r
+class Box {       // loaded by L1\r
+    Point base;\r
+    Point getBase() { return base; }\r
+}\r
+\r
+class Window {    // loaded by L2\r
+    Point size;\r
+    Point getSize() { size.win = this; return size; }\r
+}</pre></ul>\r
+\r
+<p>Since all the classes included in a class definition loaded by\r
+a class loader <code>L1</code> are also loaded by <code>L1</code>,\r
+the class of the field <code>win</code> in <code>Point</code> is\r
+now the class <code>Window</code> loaded by <code>L1</code>.\r
+Thus <code>size.win = this</code> in <code>getSize()</code> raises\r
+a runtime exception because of type mismatch; the type of\r
+<code>size.win</code> is the class <code>Point</code> loaded by\r
+<code>L1</code> whereas the type of <code>this</code> is the class\r
+<code>Point</code> loaded by <code>L2</code>.\r
+\r
+<p><br>\r
+\r
+<h3>4.2 Writing a class loader</h3>\r
+\r
+<p>A simple class loader using Javassist is as follows:\r
+\r
+<ul><pre>import javassist.*;\r
+\r
+public class SimpleLoader extends ClassLoader {\r
+    /* Call MyApp.main().\r
+     */\r
+    public static void main(String[] args) throws Throwable {\r
+        SimpleLoader s = new SimpleLoader();\r
+        Class c = s.loadClass("MyApp");\r
+        c.getDeclaredMethod("main", new Class[] { String[].class })\r
+         .invoke(null, new Object[] { args });\r
+    }\r
+\r
+    private ClassPool pool;\r
+\r
+    public SimpleLoader() throws NotFoundException {\r
+        pool = ClassPool.getDefault();\r
+        pool.insertClassPath("./class"); // <em>MyApp.class must be there.</em>\r
+    }\r
+\r
+    /* Finds a specified class.\r
+     * The bytecode for that class can be modified.\r
+     */\r
+    protected Class findClass(String name) throws ClassNotFoundException {\r
+        try {\r
+            CtClass cc = pool.get(name);\r
+            // <em>modify the CtClass object here</em>\r
+            byte[] b = pool.write(name);\r
+            return defineClass(name, b, 0, b.length);\r
+        } catch (NotFoundException e) {\r
+            throw new ClassNotFoundException();\r
+        } catch (IOException e) {\r
+            throw new ClassNotFoundException();\r
+        } catch (CannotCompileException e) {\r
+            throw new ClassNotFoundException();\r
+        }\r
+    }\r
+}</pre></ul>\r
+\r
+<p>The class <code>MyApp</code> is an application program.\r
+To execute this program, first put the class file under the\r
+<code>./class</code> directory, which must <em>not</em> be included\r
+in the class search path.  The directory name is specified by\r
+<code>insertClassPath()</code> in the constructor.\r
+You can choose a different name instead of <code>./class</code> if you want.\r
+Then do as follows:\r
+\r
+<ul><code>% java SimpleLoader</code></ul>\r
+\r
+<p>The class loader loads the class <code>MyApp</code>\r
+(<code>./class/MyApp.class</code>) and calls\r
+<code>MyApp.main()</code> with the command line parameters.\r
+Note that <code>MyApp.class</code> must not be under the directory\r
+that the system class loader searches.  Otherwise, the system class\r
+loader, which is the parent loader of <code>SimpleLoader</code>,\r
+loads the class <code>MyApp</code>.\r
+\r
+<p>This is the simplest way of using Javassist.  However, if you write\r
+a more complex class loader, you may need detailed knowledge of\r
+Java's class loading mechanism.  For example, the program above puts the\r
+<code>MyApp</code> class in a name space separated from the name space\r
+that the class <code>SimpleLoader</code> belongs to because the two\r
+classes are loaded by different class loaders.\r
+Hence, the\r
+<code>MyApp</code> class cannot directly access the class\r
+<code>SimpleLoader</code>.\r
+\r
+<p><br>\r
+\r
+<h3>4.3 Modifying a system class</h3>\r
+\r
+<p>The system classes like <code>java.lang.String</code> cannot be\r
+loaded by a class loader other than the system class loader.\r
+Therefore, <code>SimpleLoader</code> or <code>javassist.Loader</code>\r
+shown above cannot modify the system classes at loading time.\r
+\r
+<p>If your application needs to do that, the system classes must be\r
+<em>statically</em> modified.  For example, the following program\r
+adds a new field <code>hiddenValue</code> to <code>java.lang.String</code>:\r
+\r
+<ul><pre>ClassPool pool = ClassPool.getDefault();\r
+CtClass cc = pool.get("java.lang.String");\r
+cc.addField(new CtField(CtClass.intType, "hiddenValue", cc));\r
+pool.writeFile("java.lang.String", ".");</pre></ul>\r
+\r
+<p>This program produces a file <code>"./java/lang/String.class"</code>.\r
+\r
+<p>To run your program <code>MyApp</code>\r
+with this modified <code>String</code> class, do as follows:\r
+\r
+<ul><pre>\r
+% java -Xbootclasspath/p:. MyApp <i>arg1</i> <i>arg2</i>...\r
+</pre></ul>\r
+\r
+<p>Suppose that the definition of <code>MyApp</code> is as follows:\r
+\r
+<ul><pre>public class MyApp {\r
+    public static void main(String[] args) throws Exception {\r
+        System.out.println(String.class.getField("hiddenValue").getName());\r
+    }\r
+}</pre></ul>\r
+\r
+<p>If the modified <code>String</code> class is correctly loaded,\r
+<code>MyApp</code> prints <code>hiddenValue</code>.\r
+\r
+<p><i>Note: Applications that use this technique for the purpose of\r
+overriding a system class in <code>rt.jar</code> should not be\r
+deployed as doing so would contravene the Java 2 Runtime Environment\r
+binary code license.</i>\r
+\r
+<p><br>\r
+\r
+<a href="tutorial2.html">Next page</a>\r
+\r
+<hr>\r
+Java(TM) is a trademark of Sun Microsystems, Inc.<br>\r
+Copyright (C) 2000-2002 by Shigeru Chiba, All rights reserved.\r
+</body>\r
+</html>\r
diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html
new file mode 100644 (file)
index 0000000..b76996c
--- /dev/null
@@ -0,0 +1,1069 @@
+<html>\r
+<head>\r
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\r
+   <title>Javassist Tutorial</title>\r
+   <link rel="stylesheet" type="text/css" href="brown.css">\r
+</head>\r
+\r
+<body>\r
+\r
+<div align="right">Getting Started with Javassist</div>\r
+\r
+<div align="left"><a href="tutorial.html">Previous page</a></div>\r
+\r
+<a name="intro">\r
+<h2>5. Introspection and customization</h2>\r
+\r
+<p><code>CtClass</code> provides methods for introspection.  The\r
+introspective ability of Javassist is compatible with that of\r
+the Java reflection API.  <code>CtClass</code> provides\r
+<code>getName()</code>, <code>getSuperclass()</code>,\r
+<code>getMethods()</code>, and so on.\r
+<code>CtClass</code> also provides methods for modifying a class\r
+definition.  It allows to add a new field, constructor, and method.\r
+Instrumenting a method body is also possible.\r
+\r
+<p><hr width="40%">\r
+\r
+<ul>\r
+Javassist does not allow to remove a method or field, but it allows\r
+to change the name.  So if a method is not necessary any more, it should be\r
+renamed and changed to be a private method by calling\r
+<code>setName()</code>\r
+and <code>setModifiers()</code> declared in <code>CtMethod</code>.\r
+\r
+<p>Javassist does not allow to add an extra parameter to an existing\r
+method, either.  Instead of doing that, a new method receiving the\r
+extra parameter as well as the other parameters should be added to the\r
+same class.  For example, if you want to add an extra <code>int</code>\r
+parameter <code>newZ</code> to a method:\r
+\r
+<ul><pre>void move(int newX, int newY) { x = newX; y = newY; }</pre></ul>\r
+\r
+<p>in a <code>Point</code> class, then you should add the following\r
+method to the <code>Point</code> class:\r
+\r
+<ul><pre>void move(int newX, int newY, int newZ) {\r
+    // do what you want with newZ.\r
+    move(newX, newY);\r
+}</pre></ul>\r
+\r
+</ul>\r
+\r
+<p><hr width="40%">\r
+\r
+<p>Javassist also provides low-level API for directly editing a raw\r
+class file.  For example, <code>getClassFile()</code> in\r
+<code>CtClass</code> returns a <code>ClassFile</code> object\r
+representing a raw class file.  <code>getMethodInfo()</code> in\r
+<code>CtMethod</code> returns a <code>MethodInfo</code> object\r
+representing a <code>method_info</code> structure included in a class\r
+file.  The low-level API uses the vocabulary from the Java Virtual\r
+machine specification.  The users must have the knowledge about class\r
+files and bytecode.  For more details, the users should see the\r
+<code>javassist.bytecode</code> package.\r
+\r
+<p><br>\r
+\r
+<h3>5.1 Inserting source text at the beginning/end of a method body</h3>\r
+\r
+<p><code>CtMethod</code> and <code>CtConstructor</code> provide\r
+methods <code>insertBefore()</code>, <code>insertAfter()</code>, and\r
+<code>addCatch()</code>.  They are used for inserting a code fragment\r
+into the body of an existing method.  The users can specify those code\r
+fragments with <em>source text</em> written in Java.\r
+Javassist includes a simple Java compiler for processing source\r
+text.  It receives source text\r
+written in Java and compiles it into Java bytecode, which will be inserted\r
+into a method body.\r
+\r
+<p>The methods <code>insertBefore()</code>, <code>insertAfter()</code>, and\r
+<code>addCatch()</code> receives a <code>String</code> object representing\r
+a statement or a block.  A statement is a single control structure like\r
+<code>if</code> and <code>while</code> or an expression ending with\r
+a semi colon (<code>;</code>).  A block is a set of\r
+statements surrounded with braces <code>{}</code>.\r
+Hence each of the following lines is an example of valid statement or block:\r
+\r
+<ul><pre>System.out.println("Hello");\r
+{ System.out.println("Hello"); }\r
+if (i < 0) { i = -i; }\r
+</pre></ul>\r
+\r
+<p>The statement and the block can refer to fields and methods.\r
+However, they <em>cannot refer to local variables</em> declared in the\r
+method that they are inserted into.\r
+They can refer to the parameters\r
+to the method although they must use different names\r
+<code>$0</code>, <code>$1</code>, <code>$2</code>, ... described\r
+below.  Declaring a local variable in the block is allowed.\r
+\r
+<!--\r
+<p><center><table border=8 cellspacing=0 bordercolor="#cfcfcf">\r
+<tr><td bgcolor="#cfcfcf">\r
+<b>Tip:</b>\r
+<br>&nbsp&nbsp&nbsp Local variables are not accessible.&nbsp&nbsp\r
+</td></tr>\r
+</table></center>\r
+-->\r
+\r
+<p>The <code>String</code> object passed to the methods\r
+<code>insertBefore()</code>, <code>insertAfter()</code>, and\r
+<code>addCatch()</code> are compiled by\r
+the compiler included in Javassist.\r
+Since the compiler supports language extensions,\r
+several identifiers starting with <code>$</code>\r
+have special meaning:\r
+\r
+<ul><table border=0>\r
+<tr>\r
+<td><code>$0</code>, <code>$1</code>, <code>$2</code>, ... &nbsp &nbsp</td>\r
+<td>Actual parameters</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$args</code></td>\r
+<td>An array of parameters.\r
+The type of <code>$args</code> is <code>Object[]</code>.\r
+</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$$</code></td>\r
+<td rowspan=2>All actual parameters.<br>\r
+For example, <code>m($$)</code> is equivalent to\r
+<code>m($1,$2,</code>...<code>)</code></td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr>\r
+<td><code>$cflow(</code>...<code>)</code></td>\r
+<td><code>cflow</code> variable</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$r</code></td>\r
+<td>The result type.  It is used in a cast expression.</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$w</code></td>\r
+<td>The wrapper type.  It is used in a cast expression.</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$_</code></td>\r
+<td>The resulting value</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$sig</code></td>\r
+<td>An array of <code>java.lang.Class</code> objects representing\r
+the formal parameter types.\r
+</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$type</code></td>\r
+<td>A <code>java.lang.Class</code> object representing\r
+the formal result type.</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$class</code></td>\r
+<td>A <code>java.lang.Class</code> object representing\r
+the class currently edited.</td>\r
+</tr>\r
+\r
+</table>\r
+</ul>\r
+\r
+<h4>$0, $1, $2, ...</h4>\r
+\r
+<p>The parameters passed to the methods <code>insertBefore()</code>,\r
+<code>insertAfter()</code>, and <code>addCatch()</code>\r
+are accessible with\r
+<code>$0</code>, <code>$1</code>, <code>$2</code>, ... instead of\r
+the original parameter names.\r
+<code>$1</code> represents the\r
+first parameter, <code>$2</code> represents the second parameter, and\r
+so on.  The types of those variables are identical to the parameter\r
+types.\r
+<code>$0</code> is\r
+equivalent to <code>this</code>.  If the method is static,\r
+<code>$0</code> is not available.\r
+\r
+<p>These variables are used as following.  Suppose that a class\r
+<code>Point</code>:\r
+\r
+<pre><ul>class Point {\r
+    int x, y;\r
+    void move(int dx, int dy) { x += dx; y += dy; }\r
+}\r
+</ul></pre>\r
+\r
+<p>To print the values of <code>dx</code> and <code>dy</code>\r
+whenever the method <code>move()</code> is called, execute this\r
+program:\r
+\r
+<ul><pre>ClassPool pool = ClassPool.getDefault();\r
+CtClass cc = pool.get("Point");\r
+CtMethod m = cc.getDeclaredMethod("move");\r
+m.insertBefore("{ System.out.println($1); System.out.println($2); }");\r
+cc.writeFile();\r
+</pre></ul>\r
+\r
+<p>Note that the source text passed to <code>insertBefore()</code> is\r
+surrounded with braces <code>{}</code>.\r
+<code>insertBefore()</code> accepts only a single statement or a block\r
+surrounded with braces.\r
+\r
+<p>The definition of the class <code>Point</code> after the\r
+modification is like this:\r
+\r
+<pre><ul>class Point {\r
+    int x, y;\r
+    void move(int dx, int dy) {\r
+        { System.out.println(dx); System.out.println(dy); }\r
+        x += dx; y += dy;\r
+    }\r
+}\r
+</ul></pre>\r
+\r
+<p><code>$1</code> and <code>$2</code> are replaced with\r
+<code>dx</code> and <code>dy</code>, respectively.\r
+\r
+<p><code>$1</code>, <code>$2</code>, <code>$3</code> ... are\r
+updatable.  If a new value is assigend to one of those variables,\r
+then the value of the parameter represented by that variable is\r
+also updated.\r
+\r
+\r
+<h4>$args</h4>\r
+\r
+<p>The variable <code>$args</code> represents an array of all the\r
+parameters.  The type of that variable is an array of class\r
+<code>Object</code>.  If a parameter type is a primitive type such as\r
+<code>int</code>, then the parameter value is converted into a wrapper\r
+object such as <code>java.lang.Integer</code> to store in\r
+<code>$args</code>.  Thus, <code>$args[0]</code> is equivalent to\r
+<code>$1</code> unless the type of the first parameter is a primitive\r
+type.  Note that <code>$args[0]</code> is not equivalent to\r
+<code>$0</code>; <code>$0</code> represents <code>this</code>.\r
+\r
+<p>If an array of <code>Object</code> is assigned to\r
+<code>$args</code>, then each element of that array is\r
+assigned to each parameter.  If a parameter type is a primitive\r
+type, the type of the corresponding element must be a wrapper type.\r
+The value is converted from the wrapper type to the primitive type\r
+before it is assigned to the parameter.\r
+\r
+<h4>$$</h4>\r
+\r
+<p>The variable <code>$$</code> is abbreviation of a list of\r
+all the parameters separated by commas.\r
+For example, if the number of the parameters\r
+to method <code>move()</code> is three, then\r
+\r
+<ul><pre>move($$)</pre></ul>\r
+\r
+<p>is equivalent to this:\r
+\r
+<ul><pre>move($1, $2, $3)</pre></ul>\r
+\r
+<p>If <code>move()</code> does not take any parameters,\r
+then <code>move($$)</code> is\r
+equivalent to <code>move()</code>.\r
+\r
+<p><code>$$</code> can be used with another method.\r
+If you write an expression:\r
+\r
+<ul><pre>exMove($$, context)</pre></ul>\r
+\r
+<p>then this expression is equivalent to:\r
+\r
+<ul><pre>exMove($1, $2, $3, context)</pre></ul>\r
+\r
+<p>Note that <code>$$</code> enables generic notation of method call\r
+with respect to the number of parameters.\r
+It is typically used with <code>$proceed</code> shown later.\r
+\r
+<h4>$cflow</h4>\r
+\r
+<p><code>$cflow</code> means "control flow".\r
+This read-only variable returns the depth of the recursive calls\r
+to a specific method.\r
+\r
+<p>Suppose that the method shown below is represented by a\r
+<code>CtMethod</code> object <code>cm</code>:\r
+\r
+<ul><pre>int fact(int n) {\r
+    if (n <= 1)\r
+        return n;\r
+    else\r
+        return n * fact(n - 1);\r
+}</pre></ul>\r
+\r
+<p>To use <code>$cflow</code>, first declare that <code>$cflow</code>\r
+is used for monitoring calls to the method <code>fact()</code>:\r
+\r
+<ul><pre>CtMethod cm = ...;\r
+cm.useCflow("fact");</pre></ul>\r
+\r
+<p>The parameter to <code>useCflow()</code> is the identifier of the\r
+declared <code>$cflow</code> variable.  Any valid Java name can be\r
+used as the identifier.  Since the identifier can also include\r
+<code>.</code> (dot), for example, <code>"my.Test.fact"</code>\r
+is a valid identifier.\r
+\r
+<p>Then, <code>$cflow(fact)</code> represents the depth of the\r
+recursive calls to the method specified by <code>cm</code>.  The value\r
+of <code>$cflow(fact)</code> is 0 (zero) when the method is\r
+first called whereas it is 1 when the method is recursively called\r
+within the method.  For example,\r
+\r
+<ul><pre>\r
+cm.insertBefore("if ($cflow(fact) == 0)"\r
+              + "    System.out.println(\"fact \" + $1);");\r
+</pre></ul>\r
+\r
+<p>translates the method <code>fact()</code> so that it shows the\r
+parameter.  Since the value of <code>$cflow(fact)</code> is checked,\r
+the method <code>fact()</code> does not show the parameter if it is\r
+recursively called within <code>fact()</code>.\r
+\r
+<p>The value of <code>$cflow</code> is the number of stack frames\r
+associated with the specified method <code>cm</code>\r
+under the current topmost\r
+stack frame for the current thread.  <code>$cflow</code> is also\r
+accessible within a method different from the specified method\r
+<code>cm</code>.\r
+\r
+<h4>$r</h4>\r
+\r
+<p><code>$r</code> represents the result type (return type) of the method.\r
+It must be used as the cast type in a cast expression.\r
+For example, this is a typical use:\r
+\r
+<ul><pre>Object result = ... ;\r
+$_ = ($r)result;</pre></ul>\r
+\r
+<p>If the result type is a primitive type, then <code>($r)</code>\r
+converts from the wrapper type to the primitive type.\r
+For example, if the result type is <code>int</code>, then\r
+<code>($r)</code> converts from <code>java.lang.Integer</code> to\r
+<code>int</code>.\r
+\r
+<p>If the result type is <code>void</code>, then\r
+<code>($r)</code> does not convert a type; it does nothing.\r
+Moreover, the soruce text can include a <code>return</code>\r
+statement with a resulting value:\r
+\r
+<ul><pre>return ($r)result;</pre></ul>\r
+\r
+<p>Here, <code>result</code> is some local variable.\r
+Since <code>($r)</code> is specified, the resulting value is\r
+discarded.\r
+This <code>return</code> statement is regarded as the equivalent\r
+of the <code>return</code> statement without a resulting value:\r
+\r
+<ul><pre>return;</pre></ul>\r
+\r
+<h4>$w</h4>\r
+\r
+<p><code>$w</code> represents a wrapper type.\r
+It must be used as the cast type in a cast expression.\r
+<code>($w)</code> converts from a primitive type to the corresponding\r
+wrapper type.\r
+\r
+The following code is an example:\r
+\r
+<ul><pre>Integer i = ($w)5;</pre></ul>\r
+\r
+<p>The selected wrapper type depends on the type of the expression\r
+following <code>($w)</code>.  If the type of the expression is\r
+<code>double</code>, then the wrapper type is <code>java.lang.Double</code>.\r
+\r
+<p>If the type of the expression following <code>($w)</code> is not\r
+a primitive type, then <code>($w)</code> does nothing.\r
+\r
+<h4>$_</h4>\r
+\r
+<p><code>insertAfter()</code> in <code>CtMethod</code> and\r
+<code>CtConstructor</code> inserts the\r
+compiled code at the end of the method.  In the statement given to\r
+<code>insertAfter()</code>, not only the variables shown above such as\r
+<code>$0</code>, <code>$1</code>, ... but also <code>$_</code> is\r
+available.\r
+\r
+<p>The variable <code>$_</code> represents the resulting value of the\r
+method.  The type of that variable is the type of the result type (the\r
+return type) of the method.  If the result type is <code>void</code>,\r
+then the type of <code>$_</code> is <code>Object</code> and the value\r
+of <code>$_</code> is <code>null</code>.\r
+\r
+<p>Although the compiled code inserted by <code>insertAfter()</code>\r
+is executed just before the control normally returns from the method,\r
+it can be also executed when an exception is thrown from the method.\r
+To execute it when an exception is thrown, the second parameter\r
+<code>asFinally</code> to <code>insertAfter()</code> must be\r
+<code>true</code>.\r
+\r
+<p>If an exception is thrown, the compiled code inserted by\r
+<code>insertAfter()</code> is executed as a <code>finally</code>\r
+clause.  The value of <code>$_</code> is <code>0</code> or\r
+<code>null</code> in the compiled code.  After the execution of the\r
+compiled code terminates, the exception originally thrown is re-thrown\r
+to the caller.  Note that the value of <code>$_</code> is never thrown\r
+to the caller; it is rather discarded.\r
+\r
+<h4>$sig</h4>\r
+\r
+<p>The value of <code>$sig</code> is an array of\r
+<code>java.lang.Class</code> objects that represent the formal\r
+parameter types in declaration order.\r
+\r
+<h4>$type</h4>\r
+\r
+<p>The value of <code>$type</code> is an <code>java.lang.Class</code>\r
+object representing the formal type of the result value.  This\r
+variable is available only in <code>insertAfter()</code> in\r
+<code>CtMethod</code> and <code>CtConstructor</code>.\r
+\r
+<h4>$class</h4>\r
+\r
+<p>The value of <code>$class</code> is an <code>java.lang.Class</code>\r
+object representing the class in which the edited method is declared.\r
+\r
+<h4>addCatch()</h4>\r
+\r
+<p><code>addCatch()</code> inserts a code fragment into a method body\r
+so that the code fragment is executed when the method body throws\r
+an exception and the control returns to the caller.  In the source\r
+text representing the inserted code fragment, the exception value\r
+is referred to with the name specified by the third parameter to\r
+<code>addCatch()</code>.\r
+\r
+<p>For example, this program:\r
+\r
+<ul><pre>\r
+CtMethod m = ...;\r
+CtClass etype = ClassPool.getDefault().get("java.io.IOException");\r
+m.addCatch("{ System.out.println(e); throw e; }", etype, "e");\r
+</pre></ul>\r
+\r
+<p>translates the method body represented by <code>m</code> into\r
+something like this:\r
+\r
+<ul><pre>\r
+try {\r
+    <font face="serif"><em>the original method body</em></font>\r
+}\r
+catch (java.io.IOException e) {\r
+    System.out.println(e);\r
+    throw e;\r
+}\r
+</pre></ul>\r
+\r
+<p>Note that the inserted code fragment must end with a\r
+<code>throw</code> or <code>return</code> statement.\r
+\r
+<p><br>\r
+\r
+<h3>5.2 Modifying a method body</h3>\r
+\r
+<p><code>javassist.expr.ExprEditor</code> is a class\r
+for replacing an expression in a method body.\r
+The users can define a subclass of <code>ExprEditor</code>\r
+to specify how an expression is modified.\r
+\r
+<p>To run an <code>ExprEditor</code> object, the users must\r
+call <code>instrument()</code> in <code>CtMethod</code> or\r
+<code>CtClass</code>.\r
+\r
+For example,\r
+\r
+<ul><pre>\r
+CtMethod cm = ... ;\r
+cm.instrument(\r
+    new ExprEditor() {\r
+        public void edit(MethodCall m)\r
+                      throws CannotCompileException\r
+        {\r
+            if (m.getClassName().equals("Point")\r
+                          && m.getMethodName().equals("move"))\r
+                m.replace("{ $1 = 0; $_ = $proceed($$); }");\r
+        }\r
+    });\r
+</pre></ul>\r
+\r
+<p>searches the method body represented by <code>cm</code> and\r
+replaces all calls to <code>move()</code> in class <code>Point</code>\r
+with a block:\r
+\r
+<ul><pre>{ $1 = 0; $_ = $proceed($$); }\r
+</pre></ul>\r
+\r
+<p>so that the first parameter to <code>move()</code> is always 0.\r
+Note that the substituted code is not an expression but\r
+a statement or a block.\r
+\r
+<p>The method <code>instrument()</code> searches a method body.\r
+If it finds an expression such as a method call, field access, and object\r
+creation, then it calls <code>edit()</code> on the given\r
+<code>ExprEditor</code> object.  The parameter to <code>edit()</code>\r
+is an object representing the found expression.  The <code>edit()</code>\r
+method can inspect and replace the expression through that object.\r
+\r
+<p>Calling <code>replace()</code> on the parameter to <code>edit()</code>\r
+substitutes the given statement or block for the expression.  If the given\r
+block is an empty block, that is, if <code>replace("{}")</code>\r
+is executed, then the expression is removed from the method body.\r
+\r
+If you want to insert a statement (or a block) before/after the\r
+expression, a block like the following should be passed to\r
+<code>replace()</code>:\r
+\r
+<ul><pre>\r
+{ <em>before-statements;</em>\r
+  $_ = $proceed($$);\r
+  <em>after-statements;</em> }\r
+</pre></ul>\r
+\r
+<p>whichever the expression is either a method call, field access,\r
+object creation, or others.  The second statement could be:\r
+\r
+<ul><pre>$_ = $proceed();</pre></ul>\r
+\r
+<p>if the expression is read access, or\r
+\r
+<ul><pre>$proceed($$);</pre></ul>\r
+\r
+<p>if the expression is write access.\r
+\r
+<h4>javassist.expr.MethodCall</h4>\r
+\r
+<p>A <code>MethodCall</code> object represents a method call.\r
+The method <code>replace()</code> in\r
+<code>MethodCall</code> substitutes a statement or\r
+a block for the method call.\r
+It receives source text representing the substitued statement or\r
+block, in which the identifiers starting with <code>$</code>\r
+have special meaning as in the source text passed to\r
+<code>insertBefore()</code>.\r
+\r
+<ul><table border=0>\r
+<tr>\r
+<td><code>$0</code></td>\r
+<td rowspan=3>\r
+The target object of the method call.<br>\r
+This is not equivalent to <code>this</code>, which represents\r
+the caller-side <code>this</code> object.<br>\r
+<code>$0</code> is <code>null</code> if the method is static.\r
+</td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr>\r
+<td><code>$1</code>, <code>$2</code>, ... &nbsp &nbsp</td>\r
+<td>\r
+The parameters of the method call.\r
+</td>\r
+</tr>\r
+\r
+<tr><td>\r
+<code>$_</code></td>\r
+<td>The resulting value of the method call.</td>\r
+</tr>\r
+\r
+<tr><td><code>$r</code></td>\r
+<td>The result type of the method call.</td>\r
+</tr>\r
+\r
+<tr><td><code>$class</code> &nbsp &nbsp</td>\r
+<td>A <code>java.lang.Class</code> object representing\r
+the class declaring the method.\r
+</td>\r
+</tr>\r
+\r
+<tr><td><code>$sig</code> &nbsp &nbsp</td>\r
+<td>An array of <code>java.lang.Class</code> objects representing\r
+the formal parameter types.</td>\r
+</tr>\r
+\r
+<tr><td><code>$type</code> &nbsp &nbsp</td>\r
+<td>A <code>java.lang.Class</code> object representing\r
+the formal result type.</td>\r
+</tr>\r
+\r
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>\r
+<td>The name of the method originally called\r
+in the expression.</td>\r
+</tr>\r
+\r
+</table>\r
+</ul>\r
+\r
+<p>Here the method call means the one represented by the\r
+<code>MethodCall</code> object.\r
+\r
+<p>The other identifiers such as <code>$w</code>,\r
+<code>$args</code> and <code>$$</code>\r
+are also available.\r
+\r
+<p>Unless the result type of the method call is <code>void</code>,\r
+a value must be assigned to\r
+<code>$_</code> in the source text and the type of <code>$_</code>\r
+is the result type.\r
+If the result type is <code>void</code>, the type of <code>$_</code>\r
+is <code>Object</code> and the value assigned to <code>$_</code>\r
+is ignored.\r
+\r
+<p><code>$proceed</code> is not a <code>String</code> value but special\r
+syntax.  It must be followed by an argument list surrounded by parentheses\r
+<code>( )</code>.\r
+\r
+<h4>javassist.expr.FieldAccess</h4>\r
+\r
+<p>A <code>FieldAccess</code> object represents field access.\r
+The method <code>edit()</code> in <code>ExprEditor</code>\r
+receive this object if field access is found.\r
+The method <code>replace()</code> in\r
+<code>FieldAccess</code> receives\r
+source text representing the substitued statement or\r
+block for the field access.\r
+\r
+In the source text, the identifiers starting with <code>$</code>\r
+have also special meaning:\r
+\r
+<ul><table border=0>\r
+<tr>\r
+<td><code>$0</code></td>\r
+<td rowspan=3>\r
+The object containing the field accessed by the expression.\r
+This is not equivalent to <code>this</code>.<br>\r
+<code>this</code> represents the object that the method including the\r
+expression is invoked on.<br>\r
+<code>$0</code> is <code>null</code> if the field is static.\r
+</td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr>\r
+<td><code>$1</code></td>\r
+<td rowspan=2>\r
+The value that would be stored in the field\r
+if the expression is write access.\r
+<br>Otherwise, <code>$1</code> is not available.\r
+</td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr>\r
+<td><code>$_</code></td>\r
+<td rowspan=2>\r
+The resulting value of the field access\r
+if the expression is read access.\r
+<br>Otherwise, the value stored in <code>$_</code> is discarded.\r
+</td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+<tr>\r
+<td><code>$r</code></td>\r
+<td rowspan=2>\r
+The type of the field if the expression is read access.\r
+<br>Otherwise, <code>$r</code> is <code>void</code>.\r
+</td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr><td><code>$class</code> &nbsp &nbsp</td>\r
+<td>A <code>java.lang.Class</code> object representing\r
+the class declaring the field.\r
+</td></tr>\r
+\r
+<tr><td><code>$type</code></td>\r
+<td>A <code>java.lang.Class</code> object representing\r
+the field type.</td>\r
+</tr>\r
+\r
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>\r
+<td>The name of a virtual method executing the original\r
+field access.\r
+.</td>\r
+</tr>\r
+\r
+</table>\r
+</ul>\r
+\r
+<p>The other identifiers such as <code>$w</code>,\r
+<code>$args</code> and <code>$$</code>\r
+are also available.\r
+\r
+<p>If the expression is read access, a value must be assigned to\r
+<code>$_</code> in the source text.  The type of <code>$_</code>\r
+is the type of the field.\r
+\r
+<h4>javassist.expr.NewExpr</h4>\r
+\r
+<p>A <code>NewExpr</code> object represents object creation\r
+with the <code>new</code> operator.\r
+The method <code>edit()</code> in <code>ExprEditor</code>\r
+receive this object if object creation is found.\r
+The method <code>replace()</code> in\r
+<code>NewExpr</code> receives\r
+source text representing the substitued statement or\r
+block for the object creation.\r
+\r
+In the source text, the identifiers starting with <code>$</code>\r
+have also special meaning:\r
+\r
+<ul><table border=0>\r
+\r
+<tr>\r
+<td><code>$0</code></td>\r
+<td>\r
+<code>null</code>.\r
+</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$1</code>, <code>$2</code>, ... &nbsp &nbsp</td>\r
+<td>\r
+The parameters to the constructor.\r
+</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$_</code></td>\r
+<td rowspan=2>\r
+The resulting value of the object creation.\r
+<br>A newly created object must be stored in this variable.\r
+</td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr>\r
+<td><code>$r</code></td>\r
+<td>\r
+The type of the created object.\r
+</td>\r
+</tr>\r
+\r
+<tr><td><code>$class</code> &nbsp &nbsp</td>\r
+<td>A <code>java.lang.Class</code> object representing\r
+the class of the created object.\r
+</td></tr>\r
+\r
+<tr><td><code>$sig</code> &nbsp &nbsp</td>\r
+<td>An array of <code>java.lang.Class</code> objects representing\r
+the formal parameter types.</td>\r
+</tr>\r
+\r
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>\r
+<td>The name of a virtual method executing the original\r
+object creation.\r
+.</td>\r
+</tr>\r
+\r
+</table>\r
+</ul>\r
+\r
+<p>The other identifiers such as <code>$w</code>,\r
+<code>$args</code> and <code>$$</code>\r
+are also available.\r
+\r
+<h4>javassist.expr.Instanceof</h4>\r
+\r
+<p>A <code>Instanceof</code> object represents an <code>instanceof</code>\r
+expression.\r
+The method <code>edit()</code> in <code>ExprEditor</code>\r
+receive this object if an instanceof expression is found.\r
+The method <code>replace()</code> in\r
+<code>Instanceof</code> receives\r
+source text representing the substitued statement or\r
+block for the expression.\r
+\r
+In the source text, the identifiers starting with <code>$</code>\r
+have also special meaning:\r
+\r
+<ul><table border=0>\r
+\r
+<tr>\r
+<td><code>$0</code></td>\r
+<td>\r
+<code>null</code>.\r
+</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$1</code></td>\r
+<td>\r
+The value on the left hand side of the original\r
+<code>instanceof</code> operator.\r
+</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$_</code></td>\r
+<td>\r
+The resulting value of the expression.\r
+The type of <code>$_</code> is <code>boolean</code>.\r
+</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$r</code></td>\r
+<td>\r
+The type on the right hand side of the <code>instanceof</code> operator.\r
+</td>\r
+</tr>\r
+\r
+<tr><td><code>$type</code></td>\r
+<td>A <code>java.lang.Class</code> object representing\r
+the type on the right hand side of the <code>instanceof</code> operator.\r
+</td>\r
+</tr>\r
+\r
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>\r
+<td rowspan=4>The name of a virtual method executing the original\r
+<code>instanceof</code> expression.\r
+<br>It takes one parameter (the type is <code>java.lang.Object</code>)\r
+and returns true\r
+<br>if the parameter value is an instance of the type on the right\r
+hand side of\r
+<br>the original <code>instanceof</code> operator.\r
+Otherwise, it returns false.\r
+</td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+<tr><td>&nbsp</td></tr>\r
+<tr><td>&nbsp</td></tr>\r
+\r
+</table>\r
+</ul>\r
+\r
+<p>The other identifiers such as <code>$w</code>,\r
+<code>$args</code> and <code>$$</code>\r
+are also available.\r
+\r
+<h4>javassist.expr.Cast</h4>\r
+\r
+<p>A <code>Cast</code> object represents an expression for\r
+explicit type casting.\r
+The method <code>edit()</code> in <code>ExprEditor</code>\r
+receive this object if explicit type casting is found.\r
+The method <code>replace()</code> in\r
+<code>Cast</code> receives\r
+source text representing the substitued statement or\r
+block for the expression.\r
+\r
+In the source text, the identifiers starting with <code>$</code>\r
+have also special meaning:\r
+\r
+<ul><table border=0>\r
+\r
+<tr>\r
+<td><code>$0</code></td>\r
+<td>\r
+<code>null</code>.\r
+</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$1</code></td>\r
+<td>\r
+The value the type of which is explicitly cast.\r
+</td>\r
+</tr>\r
+\r
+<tr>\r
+<td><code>$_</code></td>\r
+<td rowspan=2>\r
+The resulting value of the expression.\r
+The type of <code>$_</code> is the same as the type\r
+<br>after the explicit casting, that is, the type surrounded\r
+by <code>( )</code>.\r
+</td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr>\r
+<td><code>$r</code></td>\r
+<td>the type after the explicit casting, or the type surrounded\r
+by <code>( )</code>.\r
+</td>\r
+</tr>\r
+\r
+<tr><td><code>$type</code></td>\r
+<td>A <code>java.lang.Class</code> object representing\r
+the same type as <code>$r</code>.\r
+</td>\r
+</tr>\r
+\r
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>\r
+<td rowspan=3>The name of a virtual method executing the original\r
+type casting.\r
+<br>It takes one parameter of the type <code>java.lang.Object</code>\r
+and returns it after\r
+<br>the explicit type casting specified by the original expression.\r
+\r
+</td>\r
+</tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+<tr><td>&nbsp</td></tr>\r
+\r
+</table>\r
+</ul>\r
+\r
+<p>The other identifiers such as <code>$w</code>,\r
+<code>$args</code> and <code>$$</code>\r
+are also available.\r
+\r
+<p><br>\r
+\r
+<h3>5.3 Adding a new method or field</h3>\r
+\r
+<p>Javassist allows the users to create a new method and constructor\r
+from scratch.  <code>CtNewMethod</code>\r
+and <code>CtNewConstructor</code> provide several factory methods,\r
+which are static methods for creating <code>CtMethod</code> or\r
+<code>CtConstructor</code> objects.\r
+Especially, <code>make()</code> creates \r
+a <code>CtMethod</code> or <code>CtConstructor</code> object\r
+from the given source text.\r
+\r
+<p>For example, this program:\r
+\r
+<ul><pre>\r
+CtClass point = ClassPool.getDefault().get("Point");\r
+CtMethod m = CtNewMethod.make(\r
+                 "public int xmove(int dx) { x += dx; }",\r
+                 point);\r
+point.addMethod(m);\r
+</pre></ul>\r
+\r
+<p>adds a public method <code>xmove()</code> to class <code>Point</code>.\r
+In this example, <code>x</code> is a <code>int</code> field in\r
+the class <code>Point</code>.\r
+\r
+<p>The source text passed to <code>make()</code> can refer to\r
+<code>$proceed</code> if the target object and the target method name\r
+are also given to <code>make()</code>.  For example,\r
+\r
+<ul><pre>\r
+CtClass point = ClassPool.getDefault().get("Point");\r
+CtMethod m = CtNewMethod.make(\r
+                 "public int ymove(int dy) { $proceed(0, dy); }",\r
+                 point, "this", "move");\r
+</pre></ul>\r
+\r
+<p>this program creates a method <code>ymove()</code> defined below:\r
+\r
+<ul><pre>\r
+public int ymove(int dy) { this.move(0, dy); }\r
+</pre></ul>\r
+\r
+<p>Note that <code>$proceed</code> has been replaced with\r
+<code>this.move</code>.\r
+\r
+<p>Javassist also allows the users to create a new field.\r
+\r
+<ul><pre>\r
+CtClass point = ClassPool.getDefault().get("Point");\r
+CtField f = new CtField(CtClass.intType, "z", point);\r
+point.addField(f);\r
+</pre></ul>\r
+\r
+<p>This program adds a field named <code>z</code> to class\r
+<code>Point</code>.\r
+\r
+<p>If the initial value of the added field must be specified,\r
+the program shown above must be modified into:\r
+\r
+<ul><pre>\r
+CtClass point = ClassPool.getDefault().get("Point");\r
+CtField f = new CtField(CtClass.intType, "z", point);\r
+point.addField(f, "0");    <em>// initial value is 0.</em>\r
+</pre></ul>\r
+\r
+<p>Now, the method <code>addField()</code> receives the second parameter,\r
+which is the source text representing an expression computing the initial\r
+value.  This source text can be any Java expression if the result type\r
+of the expression matches the type of the field.  Note that an expression\r
+does not end with a semi colon (<code>;</code>).\r
+\r
+<p><br>\r
+\r
+<h3>5.4 Limitations</h3>\r
+\r
+<p>In the current implementation, the Java compiler included in Javassist\r
+has several limitations with respect to the language that the compiler can\r
+accept.  Those limitations are:\r
+\r
+<p><li>The <code>.class</code> notation is not supported.  Use the\r
+method <code>Class.forName()</code>.\r
+In regular\r
+Java, an expression <code>Point.class</code> means a <code>Class</code>\r
+object representing the <code>Point</code> class.  This notation is\r
+not available.\r
+\r
+<p><li>Array initializers, a comma-separated list of expressions\r
+enclosed by braces <code>{</code> and <code>}</code>, are not\r
+supported.\r
+\r
+<p><li>Inner classes or anonymous classes are not supported.\r
+\r
+<p><li><code>switch</code> statements are not supported yet.\r
+\r
+<p><li>Labeled <code>continue</code> and <code>break</code> statements\r
+are not supported.\r
+\r
+<p><li>The <code>finally</code> clause following\r
+<code>try</code> and <code>catch</code> clauses is not supported.\r
+\r
+<p><li>The compiler does not correctly implement the Java method dispatch\r
+algorithm.  The compiler may confuse if methods defined in a class\r
+have the same name but take different parameter lists.\r
+\r
+<p><li>The users are recommended to use <code>#</code> as the separator\r
+between a class name and a static method or field name.\r
+For example, in regular Java,\r
+\r
+<ul><pre>javassist.CtClass.intType.getName()</pre></ul>\r
+\r
+<p>calls a method <code>getName()</code> on\r
+the object indicated by the static field <code>intType</code>\r
+in <code>javassist.CtClass</code>.  In Javassist, the users can\r
+write the expression shown above but they are recommended to\r
+write:\r
+\r
+<ul><pre>javassist.CtClass#intType.getName()</pre></ul>\r
+\r
+<p>so that the compiler can quickly parse the expression.\r
+</ul>\r
+\r
+<p><br>\r
+\r
+<a href="tutorial.html">Previous page</a>\r
+\r
+<hr>\r
+Java(TM) is a trademark of Sun Microsystems, Inc.<br>\r
+Copyright (C) 2000-2002 by Shigeru Chiba, All rights reserved.\r
+</body>\r
+</html>\r
diff --git a/tutorial/two.gif b/tutorial/two.gif
new file mode 100644 (file)
index 0000000..ad6984c
Binary files /dev/null and b/tutorial/two.gif differ