aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--License.html18
-rw-r--r--Readme.html453
-rw-r--r--build.xml85
-rw-r--r--sample/Test.java44
-rw-r--r--sample/duplicate/Ball.java44
-rw-r--r--sample/duplicate/DuplicatedObject.java38
-rw-r--r--sample/duplicate/Main.java44
-rw-r--r--sample/duplicate/Viewer.java78
-rw-r--r--sample/evolve/CannotCreateException.java14
-rw-r--r--sample/evolve/CannotUpdateException.java14
-rw-r--r--sample/evolve/DemoLoader.java40
-rw-r--r--sample/evolve/DemoServer.java102
-rw-r--r--sample/evolve/Evolution.java203
-rw-r--r--sample/evolve/Sample.java12
-rw-r--r--sample/evolve/VersionManager.java90
-rw-r--r--sample/evolve/WebPage.class.0bin0 -> 686 bytes
-rw-r--r--sample/evolve/WebPage.class.1bin0 -> 812 bytes
-rw-r--r--sample/evolve/WebPage.java31
-rw-r--r--sample/evolve/demo.html38
-rw-r--r--sample/evolve/start.html23
-rw-r--r--sample/evolve/update.html3
-rw-r--r--sample/reflect/Main.java33
-rw-r--r--sample/reflect/Person.java51
-rw-r--r--sample/reflect/VerboseMetaobj.java29
-rw-r--r--sample/rmi/AlertDialog.java30
-rw-r--r--sample/rmi/CountApplet.java83
-rw-r--r--sample/rmi/Counter.java32
-rw-r--r--sample/rmi/inside.gifbin0 -> 5822 bytes
-rw-r--r--sample/rmi/start.html15
-rw-r--r--sample/rmi/webdemo.html203
-rw-r--r--sample/vector/Sample.java14
-rw-r--r--sample/vector/Sample2.java13
-rw-r--r--sample/vector/Test.j38
-rw-r--r--sample/vector/VectorAssistant.java135
-rw-r--r--src/main/META-INF/MANIFEST.MF7
-rw-r--r--src/main/javassist/ByteArrayClassPath.java84
-rw-r--r--src/main/javassist/CannotCompileException.java89
-rw-r--r--src/main/javassist/ClassMap.java139
-rw-r--r--src/main/javassist/ClassPath.java57
-rw-r--r--src/main/javassist/ClassPool.java748
-rw-r--r--src/main/javassist/ClassPoolTail.java307
-rw-r--r--src/main/javassist/CodeConverter.java376
-rw-r--r--src/main/javassist/CtArray.java93
-rw-r--r--src/main/javassist/CtBehavior.java582
-rw-r--r--src/main/javassist/CtClass.java819
-rw-r--r--src/main/javassist/CtClassType.java872
-rw-r--r--src/main/javassist/CtConstructor.java458
-rw-r--r--src/main/javassist/CtField.java1107
-rw-r--r--src/main/javassist/CtMember.java79
-rw-r--r--src/main/javassist/CtMethod.java579
-rw-r--r--src/main/javassist/CtNewClass.java108
-rw-r--r--src/main/javassist/CtNewConstructor.java313
-rw-r--r--src/main/javassist/CtNewMethod.java439
-rw-r--r--src/main/javassist/CtNewWrappedConstructor.java111
-rw-r--r--src/main/javassist/CtNewWrappedMethod.java199
-rw-r--r--src/main/javassist/CtPrimitiveType.java111
-rw-r--r--src/main/javassist/Dump.java67
-rw-r--r--src/main/javassist/Loader.java340
-rw-r--r--src/main/javassist/Modifier.java191
-rw-r--r--src/main/javassist/NotFoundException.java39
-rw-r--r--src/main/javassist/SerialVersionUID.java207
-rw-r--r--src/main/javassist/Translator.java70
-rw-r--r--src/main/javassist/URLClassPath.java143
-rw-r--r--src/main/javassist/bytecode/AccessFlag.java107
-rw-r--r--src/main/javassist/bytecode/AttributeInfo.java240
-rw-r--r--src/main/javassist/bytecode/BadBytecode.java39
-rw-r--r--src/main/javassist/bytecode/ByteArray.java86
-rw-r--r--src/main/javassist/bytecode/Bytecode.java1262
-rw-r--r--src/main/javassist/bytecode/ClassFile.java543
-rw-r--r--src/main/javassist/bytecode/ClassFileWriter.java116
-rw-r--r--src/main/javassist/bytecode/CodeAttribute.java404
-rw-r--r--src/main/javassist/bytecode/CodeIterator.java743
-rw-r--r--src/main/javassist/bytecode/ConstPool.java1374
-rw-r--r--src/main/javassist/bytecode/ConstantAttribute.java82
-rw-r--r--src/main/javassist/bytecode/Descriptor.java543
-rw-r--r--src/main/javassist/bytecode/ExceptionTable.java275
-rw-r--r--src/main/javassist/bytecode/ExceptionsAttribute.java183
-rw-r--r--src/main/javassist/bytecode/FieldInfo.java185
-rw-r--r--src/main/javassist/bytecode/InnerClassesAttribute.java126
-rw-r--r--src/main/javassist/bytecode/LineNumberAttribute.java131
-rw-r--r--src/main/javassist/bytecode/LongVector.java90
-rw-r--r--src/main/javassist/bytecode/MethodInfo.java389
-rw-r--r--src/main/javassist/bytecode/Mnemonic.java251
-rw-r--r--src/main/javassist/bytecode/Opcode.java457
-rw-r--r--src/main/javassist/bytecode/SourceFileAttribute.java80
-rw-r--r--src/main/javassist/bytecode/SyntheticAttribute.java66
-rw-r--r--src/main/javassist/compiler/CodeGen.java1559
-rw-r--r--src/main/javassist/compiler/CompileError.java49
-rw-r--r--src/main/javassist/compiler/Javac.java380
-rw-r--r--src/main/javassist/compiler/JvstCodeGen.java651
-rw-r--r--src/main/javassist/compiler/KeywordTable.java42
-rw-r--r--src/main/javassist/compiler/Lex.java547
-rw-r--r--src/main/javassist/compiler/MemberCodeGen.java1045
-rw-r--r--src/main/javassist/compiler/NoFieldException.java49
-rw-r--r--src/main/javassist/compiler/Parser.java1138
-rw-r--r--src/main/javassist/compiler/ProceedHandler.java40
-rw-r--r--src/main/javassist/compiler/SymbolTable.java54
-rw-r--r--src/main/javassist/compiler/SyntaxError.java32
-rw-r--r--src/main/javassist/compiler/TokenId.java134
-rw-r--r--src/main/javassist/compiler/ast/ASTList.java169
-rw-r--r--src/main/javassist/compiler/ast/ASTree.java68
-rw-r--r--src/main/javassist/compiler/ast/AssignExpr.java50
-rw-r--r--src/main/javassist/compiler/ast/BinExpr.java48
-rw-r--r--src/main/javassist/compiler/ast/CastExpr.java63
-rw-r--r--src/main/javassist/compiler/ast/CondExpr.java47
-rw-r--r--src/main/javassist/compiler/ast/Declarator.java128
-rw-r--r--src/main/javassist/compiler/ast/DoubleConst.java50
-rw-r--r--src/main/javassist/compiler/ast/Expr.java80
-rw-r--r--src/main/javassist/compiler/ast/FieldDecl.java44
-rw-r--r--src/main/javassist/compiler/ast/InstanceOfExpr.java49
-rw-r--r--src/main/javassist/compiler/ast/IntConst.java50
-rw-r--r--src/main/javassist/compiler/ast/Keyword.java45
-rw-r--r--src/main/javassist/compiler/ast/Member.java39
-rw-r--r--src/main/javassist/compiler/ast/MethodDecl.java55
-rw-r--r--src/main/javassist/compiler/ast/NewExpr.java87
-rw-r--r--src/main/javassist/compiler/ast/Pair.java61
-rw-r--r--src/main/javassist/compiler/ast/Stmnt.java69
-rw-r--r--src/main/javassist/compiler/ast/StringL.java45
-rw-r--r--src/main/javassist/compiler/ast/Symbol.java45
-rw-r--r--src/main/javassist/compiler/ast/Variable.java48
-rw-r--r--src/main/javassist/compiler/ast/Visitor.java59
-rw-r--r--src/main/javassist/convert/TransformAfter.java56
-rw-r--r--src/main/javassist/convert/TransformBefore.java114
-rw-r--r--src/main/javassist/convert/TransformCall.java96
-rw-r--r--src/main/javassist/convert/TransformFieldAccess.java92
-rw-r--r--src/main/javassist/convert/TransformNew.java102
-rw-r--r--src/main/javassist/convert/TransformReadField.java94
-rw-r--r--src/main/javassist/convert/TransformWriteField.java82
-rw-r--r--src/main/javassist/convert/Transformer.java55
-rw-r--r--src/main/javassist/expr/Cast.java162
-rw-r--r--src/main/javassist/expr/Expr.java223
-rw-r--r--src/main/javassist/expr/ExprEditor.java211
-rw-r--r--src/main/javassist/expr/FieldAccess.java300
-rw-r--r--src/main/javassist/expr/Instanceof.java165
-rw-r--r--src/main/javassist/expr/MethodCall.java220
-rw-r--r--src/main/javassist/expr/NewExpr.java224
-rw-r--r--src/main/javassist/preproc/Assistant.java63
-rw-r--r--src/main/javassist/preproc/Compiler.java362
-rw-r--r--src/main/javassist/reflect/CannotCreateException.java39
-rw-r--r--src/main/javassist/reflect/CannotInvokeException.java75
-rw-r--r--src/main/javassist/reflect/ClassMetaobject.java312
-rw-r--r--src/main/javassist/reflect/Compiler.java169
-rw-r--r--src/main/javassist/reflect/Loader.java168
-rw-r--r--src/main/javassist/reflect/Metalevel.java48
-rw-r--r--src/main/javassist/reflect/Metaobject.java239
-rw-r--r--src/main/javassist/reflect/Reflection.java375
-rw-r--r--src/main/javassist/reflect/Sample.java66
-rw-r--r--src/main/javassist/rmi/AppletServer.java259
-rw-r--r--src/main/javassist/rmi/ObjectImporter.java308
-rw-r--r--src/main/javassist/rmi/ObjectNotFoundException.java36
-rw-r--r--src/main/javassist/rmi/Proxy.java35
-rw-r--r--src/main/javassist/rmi/RemoteException.java40
-rw-r--r--src/main/javassist/rmi/RemoteRef.java45
-rw-r--r--src/main/javassist/rmi/Sample.java46
-rw-r--r--src/main/javassist/rmi/StubGenerator.java261
-rw-r--r--src/main/javassist/runtime/Cflow.java62
-rw-r--r--src/main/javassist/runtime/Desc.java167
-rw-r--r--src/main/javassist/web/BadHttpRequest.java44
-rw-r--r--src/main/javassist/web/Viewer.java218
-rw-r--r--src/main/javassist/web/Webserver.java400
-rw-r--r--tutorial/brown.css19
-rw-r--r--tutorial/overview.gifbin0 -> 2031 bytes
-rw-r--r--tutorial/sequence.gifbin0 -> 3960 bytes
-rw-r--r--tutorial/tutorial.html560
-rw-r--r--tutorial/tutorial2.html1069
-rw-r--r--tutorial/two.gifbin0 -> 2502 bytes
166 files changed, 33356 insertions, 0 deletions
diff --git a/License.html b/License.html
new file mode 100644
index 00000000..dda30cc9
--- /dev/null
+++ b/License.html
@@ -0,0 +1,18 @@
+<HTML>
+<HEAD>
+<TITLE>Javassist License</TITLE>
+</HEAD>
+
+<BODY text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000ee bgColor=#ffffff>
+<p>
+Javassist, a Java-bytecode translator toolkit.
+Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved.
+</p>
+<p>
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+</p>
+</BODY>
+</HTML>
diff --git a/Readme.html b/Readme.html
new file mode 100644
index 00000000..cfb73123
--- /dev/null
+++ b/Readme.html
@@ -0,0 +1,453 @@
+<html>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <TITLE>Read Me First</TITLE>
+</HEAD>
+<body>
+
+<h1>Javassist version 2</h1>
+
+<h3>in February, 2003.
+<br>Copyright (C) 2000-2003 by Shigeru Chiba, All rights reserved.</h3>
+
+<p><br></p>
+
+<p>Javassist (JAVA programming ASSISTant) is yet another reflective
+system for Java. It is a class library for editing bytecodes in Java;
+it enables Java programs to define a new class at runtime and to
+modify a class file when the JVM loads it. Unlike other
+similar bytecode editors, Javassist provides two levels of API:
+source level and bytecode level. If the users use the source-level
+API, they can edit a class file without knowledge of the specifications
+of the Java bytecode. The whole API is designed with only the vocabulary
+of the Java language. On the other hand, the bytecode-level API allows
+the users to directly edit a class file.
+
+<p><br>
+
+<h2>Files</h2>
+
+<ul>
+<table>
+<tr>
+<td><li><tt><a href="License.html">License.html</a></tt></td>
+<td>License file</td>
+</tr>
+
+<tr>
+<td><li><tt><a href="tutorial/tutorial.html">tutorial/tutorial.html</a></tt></td>
+<td>Tutorial</td>
+</tr>
+
+<tr>
+<td><li><tt>./javassist.jar</tt></td>
+<td>The Javassist jar file (class files)</td>
+</tr>
+
+<tr>
+<td><li><tt>./javassist-src.zip</tt></td>
+<td>The source archive</td>
+</tr>
+
+<tr>
+<td><li><tt><a href="html/index.html">html/index.html</a></tt></tr>
+<td>The top page of the Javassist API document.</td>
+</tr>
+
+<tr>
+<td><li><tt>./sample/</tt></td>
+<td>Sample programs</td>
+</tr>
+</table>
+</ul>
+
+<p>To extract source files from the archive, use the jar command:<br>
+
+<ul><pre>% jar xvf javassist-src.zip</pre></ul>
+
+
+<p><br>
+
+<h2>How to run sample programs</h2>
+
+<p>JDK 1.2.2 or later is needed.
+
+<h3>1. Move to the directory where this Readme.html file is located.</h3>
+
+<p>In the description below, we assume that the platform is JDK 1.2
+(or later) for Solaris. If the platform is JDK 1.2 (or later) for
+Windows, the class-path option is:
+
+<ul><pre>
+-classpath ".;javassist.jar"
+</pre></ul>
+
+<p>If you don't want to put the class-path option, copy
+<tt>./javassist.jar</tt> to
+
+<ul>&lt;<i>java-home</i>&gt;<tt>/jre/lib/ext/</tt>.</ul>
+
+<p>&lt;<i>java-home</i>&gt; depends on the system. It is usually
+<tt>/usr/local/java</tt>, <tt>c:\jdk1.2\</tt>, etc.
+
+<p>If you don't use javassist.jar, first compile Javassist:
+<ul><pre>
+% cd src
+% javac -d .. javassist/*.java javassist/*/*.java
+% cd ..
+</pre></ul>
+
+<p>Then you can compile and run the sample programs without the class-path
+option.
+
+<h3>2. sample/Test.java</h3>
+
+<p> This is a very simple program using Javassist.
+
+<p> To run, type the commands:
+
+<ul><pre>
+% javac -classpath ".:javassist.jar" sample/Test.java
+% java -cp ".:javassist.jar" sample.Test
+</pre></ul>
+
+<p> For more details, see <a type="text/plain" href="sample/Test.java">sample/Test.java</a>
+
+<h3>3. sample/reflect/*.java</h3>
+
+<p> This is the "verbose metaobject" example well known in reflective
+ programming. This program dynamically attaches a metaobject to
+ a Person object. The metaobject prints a message if a method
+ is called on the Person object.
+
+<p> To run, type the commands:
+
+<ul><pre>
+% javac -classpath ".:javassist.jar" sample/reflect/*.java
+% java -cp ".:javassist.jar" javassist.reflect.Loader sample.reflect.Main Joe
+</pre></ul>
+
+<p>Compare this result with that of the regular execution without reflection:
+
+<ul><pre>% java -cp ".:javassist.jar" sample.reflect.Person Joe</pre></ul>
+
+<p> For more details, see <a type="text/plain" href="sample/reflect/Main.java">sample/reflect/Main.java</a>
+
+<p> Furthermore, the Person class can be statically modified so that
+ all the Person objects become reflective without sample.reflect.Main.
+ To do this, type the commands:
+
+<ul><pre>
+% java -cp ".:javassist.jar" javassist.reflect.Compiler sample.reflect.Person -m sample.reflect.VerboseMetaobj
+</pre></ul>
+
+<p> Then,
+<ul><pre>
+% java -cp ".:javassist.jar" sample.reflect.Person Joe
+</pre></ul>
+
+<h3>4. sample/duplicate/*.java</h3>
+
+<p> This is another example of reflective programming.
+
+<p> To run, type the commands:
+
+<ul><pre>
+% javac -classpath ".:javassist.jar" sample/duplicate/*.java
+% java -cp ".:javassist.jar" sample.duplicate.Main
+</pre></ul>
+
+<p>Compare this result with that of the regular execution without reflection:
+
+<ul><pre>% java sample.duplicate.Viewer</pre></ul>
+
+<p>For more details, see
+<a type="text/plain" href="sample/duplicate/Main.java">sample/duplicate/Main.java</a>
+
+<h3>5. sample/vector/*.java</h3>
+
+<p>This example shows the use of Javassit for producing a class representing
+a vector of a given type at compile time.
+This is a demonstration of the use of <tt>javassist.preproc</tt> package.
+
+<p> To run, type the commands:
+<ul><pre>
+% javac -classpath ".:javassist.jar" sample/vector/*.java
+% java -cp ".:javassist.jar" javassist.preproc.Compiler sample/vector/Test.j
+% javac sample/vector/Test.java
+% java sample.vector.Test
+</pre></ul>
+
+<p>For more details, see
+<a type="text/plain" href="sample/vector/Test.j">sample/vector/Test.j</a>
+and <a type="text/plain" href="sample/vector/VectorAssistant.java">sample/vector/VectorAssistant.java</a>
+
+<h3>6. sample/rmi/*.java</h3>
+
+<p> This demonstrates the javassist.rmi package.
+
+<p> To run, type the commands:
+<ul><pre>
+% javac -classpath ".:javassist.jar" sample/rmi/*.java
+% java -cp ".:javassist.jar" sample.rmi.Counter 5001
+</pre></ul>
+
+<p> The second line starts a web server listening to port 5001.
+
+<p> Then, open <a href="sample/rmi/webdemo.html">sample/rmi/webdemo.html</a>
+with a web browser running
+ on the local host. (<tt>webdemo.html</tt> trys to fetch an applet from
+ <tt>http://localhost:5001/</tt>, which is the web server we started above.)
+
+<p> Otherwise, run sample.rmi.CountApplet as an application:
+
+<ul><pre>
+% java -cp ".:javassist.jar" javassist.web.Viewer localhost 5001 sample.rmi.CountApplet
+</pre></ul>
+
+<h3>7. sample/evolve/*.java</h3>
+
+<p> This is a demonstration of the class evolution mechanism implemented
+ with Javassist. This mechanism enables a Java program to reload an
+ existing class file under some restriction.
+
+<p> To run, type the commands:
+<ul><pre>
+% javac -classpath ".:javassist.jar" sample/evolve/*.java
+% java -cp ".:javassist.jar" sample.evolve.DemoLoader 5003
+</pre></ul>
+
+<p> The second line starts a class loader DemoLoader, which runs a web
+ server DemoServer listening to port 5003.
+
+<p> Then, open <a href="http://localhost:5003/demo.html">http://localhost:5003/demo.html</a> with a web browser running
+ on the local host.
+(Or, see <a href="sample/evolve/start.html">sample/evolve/start.html</a>.)
+
+<h3>8. Hints</h3>
+
+<p>Javassist provides a class file viewer for debugging. For more details,
+see javassist.Dump.
+
+<p><br>
+
+<h2>Changes</h2>
+
+<p>- version 2.4 in February, 2003.
+<ul>
+ <li>The compiler included in Javassist did not correctly work with
+ interface methods. This bug was fixed.
+ <li>Now javassist.bytecode.Bytecode allows more than 255 local
+ variables in the same method.
+ <li>javassist.expr.Instanceof and Cast have been added.
+ <li>javassist.expr.{MethodCall,NewExpr,FieldAccess,Instanceof,Cast}.where()
+ have been added. They return the caller-side method surrounding the
+ expression.
+ <li>javassist.expr.{MethodCall,NewExpr,FieldAccess,Instanceof,Cast}.mayThrow()
+ have been added.
+ <li>$class has been introduced.
+ <li>The parameters to replaceFieldRead(), replaceFieldWrite(),
+ and redirectFieldAccess() in javassist.CodeConverter are changed.
+ <li>The compiler could not correctly handle a try-catch statement.
+ This bug has been fixed.
+</ul>
+
+<p>- version 2.3 in December, 2002.
+<ul>
+ <li>The tutorial has been revised a bit.
+ <li>SerialVersionUID class was donated by Bob Lee. Thanks.
+ <li>CtMethod.setBody() and CtConstructor.setBody() have been added.
+ <li>javassist.reflect.ClassMetaobject.useContextClassLoader has been added.
+ If true, the reflection package does not use Class.forName() but uses
+ a context class loader specified by the user.
+ <li>$sig and $type are now available.
+ <li>Bugs in Bytecode.write() and read() have been fixed.
+</ul>
+
+<p>- version 2.2 in October, 2002.
+<ul>
+ <li>The tutorial has been revised.
+ <li>A new package <code>javassist.expr</code> has been added.
+ This is replacement of classic <code>CodeConverter</code>.
+ <li>javassist.ConstParameter was changed into
+ javassist.CtMethod.ConstParameter.
+ <li>javassist.FieldInitializer was renamed into
+ javassist.CtField.Initializer.
+ <li>A bug in javassist.bytecode.Bytecode.addInvokeinterface() has been
+ fixed.
+ <li>In javassist.bytecode.Bytecode, addGetfield(), addGetstatic(),
+ addInvokespecial(), addInvokestatic(), addInvokevirtual(),
+ and addInvokeinterface()
+ have been modified to update the current statck depth.
+</ul>
+
+<p>- version 2.1 in July, 2002.
+<ul>
+ <li>javassist.CtMember and javassist.CtBehavior have been added.
+ <li>javassist.CtClass.toBytecode() has been added.
+ <li>javassist.CtClass.toClass() and javassist.ClassPool.writeAsClass()
+ has been added.
+ <li>javassist.ByteArrayClassPath has been added.
+ <li>javassist.bytecode.Mnemonic has been added.
+ <li>Several bugs have been fixed.
+</ul>
+
+<p>- version 2.0 (major update) in November, 2001.
+<ul>
+ <li>The javassist.bytecode package has been provided. It is a
+ lower-level API for directly modifying a class file although
+ the users must have detailed knowledge of the Java bytecode.
+
+ <li>The mechanism for creating CtClass objects have been changed.
+
+ <li>javassist.tool.Dump moves to the javassist package.
+</ul>
+
+<p>version 1.0 in July, 2001.
+<ul>
+ <li>javassist.reflect.Metaobject and ClassMetaobject was changed.
+ Now they throw the same exception that they receive from a
+ base-level object.
+</ul>
+
+<p>- version 0.8
+<ul>
+ <li>javassist.tool.Dump was added. It is a class file viewer.
+
+ <li>javassist.FiledInitializer.byNewArray() was added. It is for
+ initializing a field with an array object.
+
+ <li>javassist.CodeConverter.redirectMethodCall() was added.
+
+ <li>javassist.Run was added.
+</ul>
+
+<p>- version 0.7
+<ul>
+ <li>javassit.Loader was largely modified. javassist.UserLoader was
+ deleted. Instead, Codebase was renamed to ClassPath
+ and UserClassPath was added. Now programmers who want to
+ customize Loader must write a class implementing UserClassPath
+ instead of UserLoader. This change is for sharing class search paths
+ between Loader and CtClass.CtClass(String).
+
+ <li>CtClass.addField(), addMethod(), addConstructor(), addWrapper() were
+ also largely modified so that it receives CtNewMethod, CtNewConstructor,
+ or CtNewField. The static methods for creating these objects were
+ added to the API.
+
+ <li>Constructors are now represented by CtConstructor objects.
+ CtConstructor is a subclass of CtMethod.
+
+ <li>CtClass.getUserAttribute() was removed. Use CtClass.getAttribute().
+
+ <li>javassist.rmi.RmiLoader was added.
+
+ <li>javassist.reflect.Metalevel._setMetaobject() was added. Now
+ metaobjects can be replaced at runtime.
+</ul>
+
+<p>- version 0.6
+<ul>
+ <li>Javassist was modified to correctly deal with array types appearing
+ in signatures.
+
+ <li>A bug crashed resulting bytecode if a class includes a private static
+ filed. It has been fixed.
+
+ <li>javassist.CtNewInterface was added.
+
+ <li>javassist.Loader.recordClass() was renamed into makeClass().
+
+ <li>javassist.UserLoader.loadClass() was changed to take the second
+ parameter.
+</ul>
+
+<p>- version 0.5
+<ul>
+ <li>a bug-fix version.
+</ul>
+
+<p>- version 0.4
+<ul>
+ <li>Major update again. Many classes and methods were changed.
+ Most of methods taking java.lang.Class have been changed to
+ take javassist.CtClass.
+</ul>
+
+<p>- version 0.3
+<ul>
+ <li>Major update. Many classes and methods were changed.
+</ul>
+
+<p>- version 0.2
+<ul>
+ <li>Jar/zip files are supported.
+</ul>
+
+<p>-version 0.1 in April, 1999.
+<ul>
+ <li>The first release.
+</ul>
+
+<p><br>
+
+<h2>Bug reports etc.</h2>
+
+<dl>
+<dt>Bug reports:
+<dd><tt><a href="mailto:chiba@acm.org">chiba@acm.org</a></tt>
+<br>or
+<tt><a href="mailto:chiba@is.titech.ac.jp">chiba@is.titech.ac.jp</a></tt>
+<br>
+
+<p><dt>The home page of Javassist:
+<dd><tt><a href="http://www.csg.is.titech.ac.jp/~chiba/javassist">http://www.csg.is.titech.ac.jp/~chiba/javassist</a></tt>
+
+<p><dt>The Javassist mailing list:
+<dd><tt><a href="javassist@csg.is.titech.ac.jp">javassist@csg.is.titech.ac.jp</a></tt>
+</dl>
+
+<p><br>
+
+<h2>Copyright notices</h2>
+
+<p>This software is subject to the <a href="license.html">Mozilla Public
+License Version 1.1</a>.
+
+<p>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.
+
+<p>The Original Code is Javassist.
+
+<p>The Initial Developer of the Original Code is Shigeru Chiba.
+<br>Portions
+created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba.
+All Rights Reserved.
+
+<p><br>
+
+<h2>Acknowledgments</h2>
+
+<p>The development of this software is sponsored in part by the PRESTO
+program (Sakigake Kenkyu 21) of <a href="http://www.jst.go.jp/">Japan
+Science and Technology Corporation</a>.
+
+<p>I'd like to thank Michiaki Tatsubori, Johan Cloetens,
+Philip Tomlinson, Alex Villazon, Pascal Rapicault, Dan HE, Eric Tanter,
+Michael Haupt, Toshiyuki Sasaki, Renaud Pawlak, Luc Bourlier,
+Eric Bui, Lewis Stiller, Susumu Yamazaki, Rodrigo Teruo Tomita,
+Marc Segura-Devillechaise, Jan Baudisch, Julien Blass, Yoshiki Sato,
+Fabian Crabus, Bo Norregaard Jorgensen, Bob Lee, Bill Burke,
+Remy Sanlaville, Muga Nishizawa, Alexey Loubyansky, Saori Oki,
+Andreas Salathe, and Dante Torres estrada for their contributions.
+
+<p><br>
+
+<hr>
+<a href="http://www.is.titech.ac.jp/~chiba">Shigeru Chiba</a>
+(Email: <tt>chiba@is.titech.ac.jp</tt>)
+<br>Dept. of Math. and Computing Sciences,
+<a href="http://www.titech.ac.jp">Tokyo Institute of Technology</a>
diff --git a/build.xml b/build.xml
new file mode 100644
index 00000000..5d57a3a3
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+
+<!-- ======================================================================= -->
+<!-- JBoss build file -->
+<!-- ======================================================================= -->
+
+<project name="javassist" default="jar" basedir=".">
+
+ <property environment="env"/>
+ <property name="src.dir" value="${basedir}/src/main"/>
+ <property name="build.dir" value="${basedir}/build"/>
+ <property name="build.classes.dir" value="${build.dir}/classes"/>
+
+ <!-- Build classpath -->
+ <path id="classpath">
+ <pathelement location="${build.classes.dir}"/>
+ </path>
+
+ <property name="build.classpath" refid="classpath"/>
+
+ <!-- =================================================================== -->
+ <!-- Prepares the build directory -->
+ <!-- =================================================================== -->
+ <target name="prepare" >
+ <mkdir dir="${build.dir}"/>
+ <mkdir dir="${build.classes.dir}"/>
+ </target>
+
+ <!-- =================================================================== -->
+ <!-- Compiles the source code -->
+ <!-- =================================================================== -->
+ <target name="compile" depends="prepare">
+ <javac srcdir="${src.dir}"
+ destdir="${build.classes.dir}"
+ debug="on"
+ deprecation="on"
+ optimize="off"
+ includes="**">
+ <classpath refid="classpath"/>
+ </javac>
+ </target>
+
+ <target name="jar" depends="compile">
+ <jar jarfile="javassist.jar">
+ <fileset dir="${build.classes.dir}">
+ <include name="**/*.class"/>
+ </fileset>
+ </jar>
+ </target>
+
+ <target name="javadocs">
+ <javadoc packagenames="javassist.*"
+ sourcepath="src/main"
+ defaultexcludes="yes"
+ destdir="html"
+ author="true"
+ version="true"
+ use="true"
+ windowtitle="Javassist API">
+ <doctitle><![CDATA[<h1>Javassist</h1>]]></doctitle>
+ <bottom><![CDATA[<i>Javassist, a Java-bytecode translator toolkit.
+Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved.</i>]]></bottom>
+ </javadoc>
+ </target>
+
+
+ <target name="dist" depends="jar,javadocs">
+ <delete file="javassist-dist.zip"/>
+ <zip zipfile="javassist-dist.zip">
+ <fileset dir="${basedir}">
+ <include name="**"/>
+ <exclude name="build/**"/>
+ <exclude name="javassist-dist.zip"/>
+ </fileset>
+ </zip>
+ </target>
+
+ <target name="clean">
+ <delete dir="build"/>
+ <delete dir="html"/>
+ <delete file="javassist.jar"/>
+ <delete file="javassist-dist.zip"/>
+ </target>
+</project>
+
diff --git a/sample/Test.java b/sample/Test.java
new file mode 100644
index 00000000..0692943f
--- /dev/null
+++ b/sample/Test.java
@@ -0,0 +1,44 @@
+package sample;
+
+import javassist.*;
+
+/*
+ A very simple sample program
+
+ This program overwrites sample/Test.class (the class file of this
+ class itself) for adding a method g(). If the method g() is not
+ defined in class Test, then this program adds a copy of
+ f() to the class Test with name g(). Otherwise, this program does
+ not modify sample/Test.class at all.
+
+ To see the modified class definition, execute:
+
+ % javap sample.Test
+
+ after running this program.
+*/
+public class Test {
+ public int f(int i) {
+ return i + 1;
+ }
+
+ public static void main(String[] args) throws Exception {
+ ClassPool pool = ClassPool.getDefault(null);
+
+ CtClass cc = pool.get("sample.Test");
+ try {
+ cc.getDeclaredMethod("g");
+ System.out.println("g() is already defined in sample.Test.");
+ }
+ catch (NotFoundException e) {
+ /* getDeclaredMethod() throws an exception if g()
+ * is not defined in sample.Test.
+ */
+ CtMethod fMethod = cc.getDeclaredMethod("f");
+ CtMethod gMethod = CtNewMethod.copy(fMethod, "g", cc, null);
+ cc.addMethod(gMethod);
+ pool.writeFile("sample.Test"); // update the class file
+ System.out.println("g() was added.");
+ }
+ }
+}
diff --git a/sample/duplicate/Ball.java b/sample/duplicate/Ball.java
new file mode 100644
index 00000000..21d6e1cf
--- /dev/null
+++ b/sample/duplicate/Ball.java
@@ -0,0 +1,44 @@
+package sample.duplicate;
+
+import java.awt.Graphics;
+import java.awt.Color;
+
+public class Ball {
+ private int x, y;
+ private Color color;
+ private int radius = 30;
+ private boolean isBackup = false;
+
+ public Ball(int x, int y) {
+ move(x, y);
+ changeColor(Color.orange);
+ }
+
+ // This constructor is for a backup object.
+ public Ball(Ball b) {
+ isBackup = true;
+ }
+
+ // Adjust the position so that the backup object is visible.
+ private void adjust() {
+ if (isBackup) {
+ this.x += 50;
+ this.y += 50;
+ }
+ }
+
+ public void paint(Graphics g) {
+ g.setColor(color);
+ g.fillOval(x, y, radius, radius);
+ }
+
+ public void move(int x, int y) {
+ this.x = x;
+ this.y = y;
+ adjust();
+ }
+
+ public void changeColor(Color color) {
+ this.color = color;
+ }
+}
diff --git a/sample/duplicate/DuplicatedObject.java b/sample/duplicate/DuplicatedObject.java
new file mode 100644
index 00000000..5995abcc
--- /dev/null
+++ b/sample/duplicate/DuplicatedObject.java
@@ -0,0 +1,38 @@
+package sample.duplicate;
+
+import javassist.reflect.*;
+
+public class DuplicatedObject extends Metaobject {
+ private DuplicatedObject backup;
+
+ // if a base-level object is created, this metaobject creates
+ // a copy of the base-level object.
+
+ public DuplicatedObject(Object self, Object[] args) {
+ super(self, args);
+ ClassMetaobject clazz = getClassMetaobject();
+ if (clazz.isInstance(args[0]))
+ backup = null; // self is a backup object.
+ else {
+ Object[] args2 = new Object[1];
+ args2[0] = self;
+ try {
+ Metalevel m = (Metalevel)clazz.newInstance(args2);
+ backup = (DuplicatedObject)m._getMetaobject();
+ }
+ catch (CannotCreateException e) {
+ backup = null;
+ }
+ }
+ }
+
+ public Object trapMethodcall(int identifier, Object[] args)
+ throws Throwable
+ {
+ Object obj = super.trapMethodcall(identifier, args);
+ if (backup != null)
+ backup.trapMethodcall(identifier, args);
+
+ return obj;
+ }
+}
diff --git a/sample/duplicate/Main.java b/sample/duplicate/Main.java
new file mode 100644
index 00000000..e152a23e
--- /dev/null
+++ b/sample/duplicate/Main.java
@@ -0,0 +1,44 @@
+package sample.duplicate;
+
+/*
+ Runtime metaobject (JDK 1.2 or later only).
+
+ With the javassist.reflect package, the users can attach a metaobject
+ to an object. The metaobject can control the behavior of the object.
+ For example, you can implement fault tolerancy with this ability. One
+ of the implementation techniques of fault tolernacy is to make a copy
+ of every object containing important data and maintain it as a backup.
+ If the machine running the object becomes down, the backup object on a
+ different machine is used to continue the execution.
+
+ To make the copy of the object a real backup, all the method calls to
+ the object must be also sent to that copy. The metaobject is needed
+ for this duplication of the method calls. It traps every method call
+ and invoke the same method on the copy of the object so that the
+ internal state of the copy is kept equivalent to that of the original
+ object.
+
+ First, run sample.duplicate.Viewer without a metaobject.
+
+ % java sample.duplicate.Viewer
+
+ This program shows a ball in a window.
+
+ Then, run the same program with a metaobject, which is an instance
+ of sample.duplicate.DuplicatedObject.
+
+ % java sample.duplicate.Main
+
+ You would see two balls in a window. This is because
+ sample.duplicate.Viewer is loaded by javassist.reflect.Loader so that
+ a metaobject would be attached.
+*/
+public class Main {
+ public static void main(String[] args) throws Throwable {
+ javassist.reflect.Loader cl = new javassist.reflect.Loader();
+ cl.makeReflective("sample.duplicate.Ball",
+ "sample.duplicate.DuplicatedObject",
+ "javassist.reflect.ClassMetaobject");
+ cl.run("sample.duplicate.Viewer", args);
+ }
+}
diff --git a/sample/duplicate/Viewer.java b/sample/duplicate/Viewer.java
new file mode 100644
index 00000000..aec13f6b
--- /dev/null
+++ b/sample/duplicate/Viewer.java
@@ -0,0 +1,78 @@
+package sample.duplicate;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+
+public class Viewer extends Applet
+ implements MouseListener, ActionListener, WindowListener
+{
+ private static final Color[] colorList = {
+ Color.orange, Color.pink, Color.green, Color.blue };
+
+ private Ball ball;
+ private int colorNo;
+
+ public void init() {
+ colorNo = 0;
+ Button b = new Button("change");
+ b.addActionListener(this);
+ add(b);
+
+ addMouseListener(this);
+ }
+
+ public void start() {
+ ball = new Ball(50, 50);
+ ball.changeColor(colorList[0]);
+ }
+
+ public void paint(Graphics g) {
+ ball.paint(g);
+ }
+
+ public void mouseClicked(MouseEvent ev) {
+ ball.move(ev.getX(), ev.getY());
+ repaint();
+ }
+
+ public void mouseEntered(MouseEvent ev) {}
+
+ public void mouseExited(MouseEvent ev) {}
+
+ public void mousePressed(MouseEvent ev) {}
+
+ public void mouseReleased(MouseEvent ev) {}
+
+ public void actionPerformed(ActionEvent e) {
+ ball.changeColor(colorList[++colorNo % colorList.length]);
+ repaint();
+ }
+
+ public void windowOpened(WindowEvent e) {}
+
+ public void windowClosing(WindowEvent e) {
+ System.exit(0);
+ }
+
+ public void windowClosed(WindowEvent e) {}
+
+ public void windowIconified(WindowEvent e) {}
+
+ public void windowDeiconified(WindowEvent e) {}
+
+ public void windowActivated(WindowEvent e) {}
+
+ public void windowDeactivated(WindowEvent e) {}
+
+ public static void main(String[] args) {
+ Frame f = new Frame("Viewer");
+ Viewer view = new Viewer();
+ f.addWindowListener(view);
+ f.add(view);
+ f.setSize(300, 300);
+ view.init();
+ view.start();
+ f.setVisible(true);
+ }
+}
diff --git a/sample/evolve/CannotCreateException.java b/sample/evolve/CannotCreateException.java
new file mode 100644
index 00000000..828afb06
--- /dev/null
+++ b/sample/evolve/CannotCreateException.java
@@ -0,0 +1,14 @@
+package sample.evolve;
+
+/**
+ * Signals that VersionManager.newInstance() fails.
+ */
+public class CannotCreateException extends RuntimeException {
+ public CannotCreateException(String s) {
+ super(s);
+ }
+
+ public CannotCreateException(Exception e) {
+ super("by " + e.toString());
+ }
+}
diff --git a/sample/evolve/CannotUpdateException.java b/sample/evolve/CannotUpdateException.java
new file mode 100644
index 00000000..bd28bba6
--- /dev/null
+++ b/sample/evolve/CannotUpdateException.java
@@ -0,0 +1,14 @@
+package sample.evolve;
+
+/**
+ * Signals that VersionManager.update() fails.
+ */
+public class CannotUpdateException extends Exception {
+ public CannotUpdateException(String msg) {
+ super(msg);
+ }
+
+ public CannotUpdateException(Exception e) {
+ super("by " + e.toString());
+ }
+}
diff --git a/sample/evolve/DemoLoader.java b/sample/evolve/DemoLoader.java
new file mode 100644
index 00000000..5fa950da
--- /dev/null
+++ b/sample/evolve/DemoLoader.java
@@ -0,0 +1,40 @@
+package sample.evolve;
+
+import javassist.*;
+import java.io.*;
+import java.util.Hashtable;
+
+/**
+ * DemoLoader is a class loader for running a program including
+ * an updatable class. This simple loader allows only a single
+ * class to be updatable. (Extending it for supporting multiple
+ * updatable classes is easy.)
+ *
+ * To run, type as follows:
+ *
+ * % java sample.evolve.DemoLoader <port number>
+ *
+ * Then DemoLoader launches sample.evolve.DemoServer with <port number>.
+ * It also translates sample.evolve.WebPage, which sample.evolve.DemoServer
+ * uses, so that it is an updable class.
+ *
+ * Note: JDK 1.2 or later only.
+ */
+public class DemoLoader {
+ private static final int initialVersion = 0;
+ private String updatableClassName = null;
+ private CtClass updatableClass = null;
+
+ /* Creates a <code>DemoLoader</code> for making class WebPage
+ * updatable. Then it runs main() in sample.evolve.DemoServer.
+ */
+ public static void main(String[] args) throws Throwable {
+ Evolution translator = new Evolution();
+ ClassPool cp = ClassPool.getDefault(translator);
+ Loader cl = new Loader();
+ cl.setClassPool(cp);
+
+ translator.makeUpdatable("sample.evolve.WebPage");
+ cl.run("sample.evolve.DemoServer", args);
+ }
+}
diff --git a/sample/evolve/DemoServer.java b/sample/evolve/DemoServer.java
new file mode 100644
index 00000000..943c509c
--- /dev/null
+++ b/sample/evolve/DemoServer.java
@@ -0,0 +1,102 @@
+package sample.evolve;
+
+import javassist.*;
+import javassist.web.*;
+import java.io.*;
+
+/**
+ * A web server for demonstrating class evolution. It must be
+ * run with a DemoLoader.
+ *
+ * If a html file /java.html is requested, this web server calls
+ * WebPage.show() for constructing the contents of that html file
+ * So if a DemoLoader changes the definition of WebPage, then
+ * the image of /java.html is also changed.
+ * Note that WebPage is not an applet. It is rather
+ * similar to a CGI script or a servlet. The web server never
+ * sends the class file of WebPage to web browsers.
+ *
+ * Furthermore, if a html file /update.html is requested, this web
+ * server overwrites WebPage.class (class file) and calls update()
+ * in VersionManager so that WebPage.class is loaded into the JVM
+ * again. The new contents of WebPage.class are copied from
+ * either WebPage.class.0 or WebPage.class.1.
+ */
+public class DemoServer extends Webserver {
+
+ public static void main(String[] args) throws IOException
+ {
+ if (args.length == 1) {
+ DemoServer web = new DemoServer(Integer.parseInt(args[0]));
+ web.run();
+ }
+ else
+ System.err.println(
+ "Usage: java sample.evolve.DemoServer <port number>");
+ }
+
+ public DemoServer(int port) throws IOException {
+ super(port);
+ htmlfileBase = "sample/evolve/";
+ }
+
+ private static final String ver0 = "sample/evolve/WebPage.class.0";
+ private static final String ver1 = "sample/evolve/WebPage.class.1";
+ private String currentVersion = ver0;
+
+ public void doReply(InputStream in, OutputStream out, String cmd)
+ throws IOException, BadHttpRequest
+ {
+ if (cmd.startsWith("GET /java.html ")) {
+ runJava(out);
+ return;
+ }
+ else if (cmd.startsWith("GET /update.html ")) {
+ try {
+ if (currentVersion == ver0)
+ currentVersion = ver1;
+ else
+ currentVersion = ver0;
+
+ updateClassfile(currentVersion);
+ VersionManager.update("sample.evolve.WebPage");
+ }
+ catch (CannotUpdateException e) {
+ logging(e.toString());
+ }
+ catch (FileNotFoundException e) {
+ logging(e.toString());
+ }
+ }
+
+ super.doReply(in, out, cmd);
+ }
+
+ private void runJava(OutputStream outs) throws IOException {
+ OutputStreamWriter out = new OutputStreamWriter(outs);
+ out.write("HTTP/1.0 200 OK\r\n\r\n");
+ WebPage page = new WebPage();
+ page.show(out);
+ out.close();
+ }
+
+ /* updateClassfile() copies the specified file to WebPage.class.
+ */
+ private void updateClassfile(String filename)
+ throws IOException, FileNotFoundException
+ {
+ byte[] buf = new byte[1024];
+
+ FileInputStream fin
+ = new FileInputStream(filename);
+ FileOutputStream fout
+ = new FileOutputStream("sample/evolve/WebPage.class");
+ for (;;) {
+ int len = fin.read(buf);
+ if (len >= 0)
+ fout.write(buf, 0, len);
+ else
+ break;
+ }
+ }
+}
diff --git a/sample/evolve/Evolution.java b/sample/evolve/Evolution.java
new file mode 100644
index 00000000..810f1986
--- /dev/null
+++ b/sample/evolve/Evolution.java
@@ -0,0 +1,203 @@
+package sample.evolve;
+
+import javassist.*;
+import java.io.IOException;
+
+/**
+ * Evolution provides a set of methods for instrumenting bytecodes.
+ *
+ * For class evolution, updatable class A is renamed to B. Then an
+ * abstract class named A is produced as the super class of B. If the
+ * original class A has a public method m(), then the abstract class A
+ * has an abstract method m().
+ *
+ * abstract class A
+ * abstract m()
+ * _makeInstance()
+ * |
+ * class A --------> class B
+ * m() m()
+ *
+ * Also, all the other classes are translated so that "new A(i)"
+ * in the methods is replaced with "_makeInstance(i)". This makes
+ * it possible to change the behavior of the instantiation of
+ * the class A.
+ */
+public class Evolution implements Translator {
+ public final static String handlerMethod = "_makeInstance";
+ public final static String latestVersionField
+ = VersionManager.latestVersionField;
+ public final static String versionManagerMethod = "initialVersion";
+
+ private static CtMethod trapMethod;
+ private static final int initialVersion = 0;
+ private ClassPool pool;
+ private String updatableClassName = null;
+ private CtClass updatableClass = null;
+
+ public void start(ClassPool _pool) throws NotFoundException {
+ pool = _pool;
+
+ // Get the definition of Sample.make() and store it into trapMethod
+ // for later use.
+ trapMethod = _pool.getMethod("sample.evolve.Sample", "make");
+ }
+
+ public void onWrite(ClassPool _pool, String classname)
+ throws NotFoundException, CannotCompileException
+ {
+ onWriteUpdatable(classname);
+
+ /*
+ * Replaces all the occurrences of the new operator with a call
+ * to _makeInstance().
+ */
+ CtClass clazz = _pool.get(classname);
+ CtClass absClass = updatableClass;
+ CodeConverter converter = new CodeConverter();
+ converter.replaceNew(absClass, absClass, handlerMethod);
+ clazz.instrument(converter);
+ }
+
+ private void onWriteUpdatable(String classname)
+ throws NotFoundException, CannotCompileException
+ {
+ // if the class is a concrete class,
+ // classname is <updatableClassName>$<version>.
+
+ int i = classname.lastIndexOf('$');
+ if (i <= 0)
+ return;
+
+ String orgname = classname.substring(0, i);
+ if (!orgname.equals(updatableClassName))
+ return;
+
+ int version;
+ try {
+ version = Integer.parseInt(classname.substring(i + 1));
+ }
+ catch (NumberFormatException e) {
+ throw new NotFoundException(classname, e);
+ }
+
+
+ CtClass clazz = pool.getAndRename(orgname, classname);
+ makeConcreteClass(clazz, updatableClass, version);
+ }
+
+ /* Register an updatable class.
+ */
+ public void makeUpdatable(String classname)
+ throws NotFoundException, CannotCompileException
+ {
+ if (pool == null)
+ throw new RuntimeException(
+ "Evolution has not been linked to ClassPool.");
+
+ CtClass c = pool.get(classname);
+ updatableClassName = classname;
+ updatableClass = makeAbstractClass(c);
+ }
+
+ /**
+ * Produces an abstract class.
+ */
+ protected CtClass makeAbstractClass(CtClass clazz)
+ throws CannotCompileException, NotFoundException
+ {
+ int i;
+
+ CtClass absClass = pool.makeClass(clazz.getName());
+ absClass.setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT);
+ absClass.setSuperclass(clazz.getSuperclass());
+ absClass.setInterfaces(clazz.getInterfaces());
+
+ // absClass.inheritAllConstructors();
+
+ CtField fld = new CtField(pool.get("java.lang.Class"),
+ latestVersionField, absClass);
+ fld.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
+
+ CtField.Initializer finit
+ = CtField.Initializer.byCall(
+ pool.get("sample.evolve.VersionManager"),
+ versionManagerMethod,
+ new String[] { clazz.getName() });
+ absClass.addField(fld, finit);
+
+ CtField[] fs = clazz.getDeclaredFields();
+ for (i = 0; i < fs.length; ++i) {
+ CtField f = fs[i];
+ if (Modifier.isPublic(f.getModifiers()))
+ absClass.addField(new CtField(f.getType(), f.getName(),
+ absClass));
+ }
+
+ CtConstructor[] cs = clazz.getDeclaredConstructors();
+ for (i = 0; i < cs.length; ++i) {
+ CtConstructor c = cs[i];
+ int mod = c.getModifiers();
+ if (Modifier.isPublic(mod)) {
+ CtMethod wm
+ = CtNewMethod.wrapped(absClass, handlerMethod,
+ c.getParameterTypes(),
+ c.getExceptionTypes(),
+ trapMethod, null, absClass);
+ wm.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
+ absClass.addMethod(wm);
+ }
+ }
+
+ CtMethod[] ms = clazz.getDeclaredMethods();
+ for (i = 0; i < ms.length; ++i) {
+ CtMethod m = ms[i];
+ int mod = m.getModifiers();
+ if (Modifier.isPublic(mod))
+ if (Modifier.isStatic(mod))
+ throw new CannotCompileException(
+ "static methods are not supported.");
+ else {
+ CtMethod m2
+ = CtNewMethod.abstractMethod(m.getReturnType(),
+ m.getName(),
+ m.getParameterTypes(),
+ m.getExceptionTypes(),
+ absClass);
+ absClass.addMethod(m2);
+ }
+ }
+
+ return absClass;
+ }
+
+ /**
+ * Modifies the given class file so that it is a subclass of the
+ * abstract class produced by makeAbstractClass().
+ *
+ * Note: the naming convention must be consistent with
+ * VersionManager.update().
+ */
+ protected void makeConcreteClass(CtClass clazz,
+ CtClass abstractClass, int version)
+ throws CannotCompileException, NotFoundException
+ {
+ int i;
+ clazz.setSuperclass(abstractClass);
+ CodeConverter converter = new CodeConverter();
+ CtField[] fs = clazz.getDeclaredFields();
+ for (i = 0; i < fs.length; ++i) {
+ CtField f = fs[i];
+ if (Modifier.isPublic(f.getModifiers()))
+ converter.redirectFieldAccess(f, abstractClass, f.getName());
+ }
+
+ CtConstructor[] cs = clazz.getDeclaredConstructors();
+ for (i = 0; i < cs.length; ++i)
+ cs[i].instrument(converter);
+
+ CtMethod[] ms = clazz.getDeclaredMethods();
+ for (i = 0; i < ms.length; ++i)
+ ms[i].instrument(converter);
+ }
+}
diff --git a/sample/evolve/Sample.java b/sample/evolve/Sample.java
new file mode 100644
index 00000000..c147c965
--- /dev/null
+++ b/sample/evolve/Sample.java
@@ -0,0 +1,12 @@
+package sample.evolve;
+
+/**
+ * This is a sample class used by Transformer.
+ */
+public class Sample {
+ public static Class _version;
+
+ public static Object make(Object[] args) {
+ return VersionManager.make(_version, args);
+ }
+}
diff --git a/sample/evolve/VersionManager.java b/sample/evolve/VersionManager.java
new file mode 100644
index 00000000..d95268b8
--- /dev/null
+++ b/sample/evolve/VersionManager.java
@@ -0,0 +1,90 @@
+package sample.evolve;
+
+import java.util.Hashtable;
+import java.lang.reflect.*;
+import javassist.CtClass;
+
+/**
+ * Runtime system for class evolution
+ */
+public class VersionManager {
+ private static Hashtable versionNo = new Hashtable();
+ public final static String latestVersionField = "_version";
+
+ /**
+ * For updating the definition of class my.X, say:
+ *
+ * VersionManager.update("my.X");
+ */
+ public static void update(String qualifiedClassname)
+ throws CannotUpdateException
+ {
+ try {
+ Class c = getUpdatedClass(qualifiedClassname);
+ Field f = c.getField(latestVersionField);
+ f.set(null, c);
+ }
+ catch (ClassNotFoundException e) {
+ throw new CannotUpdateException("cannot update class: "
+ + qualifiedClassname);
+ }
+ catch (Exception e) {
+ throw new CannotUpdateException(e);
+ }
+ }
+
+ private static Class getUpdatedClass(String qualifiedClassname)
+ throws ClassNotFoundException
+ {
+ int version;
+ Object found = versionNo.get(qualifiedClassname);
+ if (found == null)
+ version = 0;
+ else
+ version = ((Integer)found).intValue() + 1;
+
+ Class c = Class.forName(qualifiedClassname + '$' + version);
+ versionNo.put(qualifiedClassname, new Integer(version));
+ return c;
+ }
+
+ /* initiaVersion() is used to initialize the _version field of
+ * the updatable classes.
+ */
+ public static Class initialVersion(String[] params) {
+ try {
+ return getUpdatedClass(params[0]);
+ }
+ catch (ClassNotFoundException e) {
+ throw new RuntimeException("cannot initialize " + params[0]);
+ }
+ }
+
+ /** make() performs the object creation of the updatable classes.
+ * The expression "new <updatable class>" is replaced with a call
+ * to this method.
+ */
+ public static Object make(Class clazz, Object[] args) {
+ Constructor[] constructors = clazz.getConstructors();
+ 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");
+ }
+}
diff --git a/sample/evolve/WebPage.class.0 b/sample/evolve/WebPage.class.0
new file mode 100644
index 00000000..3cc1d743
--- /dev/null
+++ b/sample/evolve/WebPage.class.0
Binary files differ
diff --git a/sample/evolve/WebPage.class.1 b/sample/evolve/WebPage.class.1
new file mode 100644
index 00000000..fe49380e
--- /dev/null
+++ b/sample/evolve/WebPage.class.1
Binary files differ
diff --git a/sample/evolve/WebPage.java b/sample/evolve/WebPage.java
new file mode 100644
index 00000000..7c2b7cfb
--- /dev/null
+++ b/sample/evolve/WebPage.java
@@ -0,0 +1,31 @@
+package sample.evolve;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Updatable class. DemoServer instantiates this class and calls
+ * show() on the created object.
+ */
+
+// WebPage.class.0
+public class WebPage {
+ public void show(OutputStreamWriter out) throws IOException {
+ Calendar c = new GregorianCalendar();
+ out.write(c.getTime().toString());
+ out.write("<P><A HREF=\"demo.html\">Return to the home page.</A>");
+ }
+}
+/*
+// WebPage.class.1
+public class WebPage {
+ public void show(OutputStreamWriter out) throws IOException {
+ out.write("<H2>Current Time:</H2>");
+ Calendar c = new GregorianCalendar();
+ out.write("<CENTER><H3><FONT color=\"blue\">");
+ out.write(c.getTime().toString());
+ out.write("</FONT></H3></CENTER><HR>");
+ out.write("<P><A HREF=\"demo.html\">Return to the home page.</A>");
+ }
+}
+*/
diff --git a/sample/evolve/demo.html b/sample/evolve/demo.html
new file mode 100644
index 00000000..6be4a2c3
--- /dev/null
+++ b/sample/evolve/demo.html
@@ -0,0 +1,38 @@
+<H2>Class Evolution</H2>
+
+<P>This is a demonstration of the class evolution mechanism
+implemented with Javassist. This mechanism enables a Java program to
+reload an existing class file. Although the reloaded class file is
+not applied to exiting objects (the old class file is used for those
+objects), it is effective in newly created objects.
+
+<P>Since the reloading is transparently executed, no programming
+convention is needed. However, there are some limitations on possible
+changes of the class definition. For example, the new class definition
+must have the same set of methods as the old one. These limitations are
+necessary for keeping the type system consistent.
+
+
+<H3><a href="java.html">Run WebPage.show()</a></H3>
+
+<P>The web server creates a new <code>WebPage</code> object and
+calls <code>show()</code> on that object. This method works as
+if it is a CGI script or a servlet and you will see the html file
+produced by this method on your browser.
+
+<H3><a href="update.html">Change WebPage.class</a></H3>
+
+<P>The web server overwrites class file <code>WebPage.class</code>
+on the local disk. Then it signals that <code>WebPage.class</code>
+must be reloaded into the JVM. If you run <code>WebPage.show()</code>
+again, you will see a different page on your browser.
+
+<H3>Source files</H3>
+
+<P>Web server: <A HREF="DemoServer.java"><code>DemoServer.java</code></A>
+
+<P>WebPage: <A HREF="WebPage.java"><code>WebPage.java</code></A>
+
+<P>Class loader: <A HREF="DemoLoader.java"><code>DemoLoader.java</code></A>,
+ <A HREF="Evolution.java"><code>Evolution.java</code></A>, and
+ <A HREF="VersionManager.java"><code>VersionManager.java</code></A>.
diff --git a/sample/evolve/start.html b/sample/evolve/start.html
new file mode 100644
index 00000000..d31d9d08
--- /dev/null
+++ b/sample/evolve/start.html
@@ -0,0 +1,23 @@
+<h2>Instructions</h2>
+
+<p>1. Compile <code>sample/evolve/*.java</code>.
+ Copy <code>WebPage.class</code> to <code>WebPage.class.0</code>.
+
+<p>2. Edit <code>Webpage.java</code>, compile it,
+ and copy <code>WebPage.class</code> to <code>WebPage.class.1</code>.
+<br><code>WebPage.class.0</code> and
+ <code>WebPage.class.1</code> are used
+ for changing the contents of <code>WebPage.class</code> during
+ the demo.
+
+<p>3. Run the server on the local host (where your web browser is running):
+
+<ul>
+<code>% java sample.evolve.DemoLoader 5003
+</code></ul>
+
+<p>4. Click below:
+
+<ul><h2><a href="http://localhost:5003/demo.html">
+Start!
+</a></h2></ul>
diff --git a/sample/evolve/update.html b/sample/evolve/update.html
new file mode 100644
index 00000000..82551653
--- /dev/null
+++ b/sample/evolve/update.html
@@ -0,0 +1,3 @@
+<h2><code>WebPage.class</code> has been changed.</h2>
+
+<a href="demo.html">Return to the home page.</a>
diff --git a/sample/reflect/Main.java b/sample/reflect/Main.java
new file mode 100644
index 00000000..6086d9f8
--- /dev/null
+++ b/sample/reflect/Main.java
@@ -0,0 +1,33 @@
+package sample.reflect;
+
+import javassist.reflect.ClassMetaobject;
+import javassist.reflect.Loader;
+
+/*
+ The "verbose metaobject" example (JDK 1.2 or later only).
+
+ Since this program registers class Person as a reflective class
+ (in a more realistic demonstration, what classes are reflective
+ would be specified by some configuration file), the class loader
+ modifies Person.class when loading into the JVM so that the class
+ Person is changed into a reflective class and a Person object is
+ controlled by a VerboseMetaobj.
+
+ To run,
+
+ % java javassist.reflect.Loader sample.reflect.Main Joe
+
+ Compare this result with that of the regular execution without reflection:
+
+ % java sample.reflect.Person Joe
+*/
+public class Main {
+ public static void main(String[] args) throws Throwable {
+ Loader cl = (Loader)Main.class.getClassLoader();
+ cl.makeReflective("sample.reflect.Person",
+ "sample.reflect.VerboseMetaobj",
+ "javassist.reflect.ClassMetaobject");
+
+ cl.run("sample.reflect.Person", args);
+ }
+}
diff --git a/sample/reflect/Person.java b/sample/reflect/Person.java
new file mode 100644
index 00000000..5e0e2ad8
--- /dev/null
+++ b/sample/reflect/Person.java
@@ -0,0 +1,51 @@
+/*
+ A base-level class controlled by VerboseMetaobj.
+*/
+
+package sample.reflect;
+
+import javassist.reflect.Metalevel;
+import javassist.reflect.Metaobject;
+
+public class Person {
+ public String name;
+ public static int birth = 3;
+ public static final String defaultName = "John";
+
+ public Person(String name, int birthYear) {
+ if (name == null)
+ this.name = defaultName;
+ else
+ this.name = name;
+
+ this.birth = birthYear;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge(int year) {
+ return year - birth;
+ }
+
+ public static void main(String[] args) {
+ String name;
+ if (args.length > 0)
+ name = args[0];
+ else
+ name = "Bill";
+
+ Person p = new Person(name, 1960);
+ System.out.println("name: " + p.getName());
+ System.out.println("object: " + p.toString());
+
+ // change the metaobject of p.
+ if (p instanceof Metalevel) {
+ ((Metalevel)p)._setMetaobject(new Metaobject(p, null));
+ System.out.println("<< the metaobject was changed.>>");
+ }
+
+ System.out.println("age: " + p.getAge(1999));
+ }
+}
diff --git a/sample/reflect/VerboseMetaobj.java b/sample/reflect/VerboseMetaobj.java
new file mode 100644
index 00000000..a9f75dd1
--- /dev/null
+++ b/sample/reflect/VerboseMetaobj.java
@@ -0,0 +1,29 @@
+package sample.reflect;
+
+import javassist.*;
+import javassist.reflect.*;
+
+public class VerboseMetaobj extends Metaobject {
+ public VerboseMetaobj(Object self, Object[] args) {
+ super(self, args);
+ System.out.println("** constructed: " + self.getClass().getName());
+ }
+
+ public Object trapFieldRead(String name) {
+ System.out.println("** field read: " + name);
+ return super.trapFieldRead(name);
+ }
+
+ public void trapFieldWrite(String name, Object value) {
+ System.out.println("** field write: " + name);
+ super.trapFieldWrite(name, value);
+ }
+
+ public Object trapMethodcall(int identifier, Object[] args)
+ throws Throwable
+ {
+ System.out.println("** trap: " + getMethodName(identifier)
+ + "() in " + getClassMetaobject().getName());
+ return super.trapMethodcall(identifier, args);
+ }
+}
diff --git a/sample/rmi/AlertDialog.java b/sample/rmi/AlertDialog.java
new file mode 100644
index 00000000..99fae5c2
--- /dev/null
+++ b/sample/rmi/AlertDialog.java
@@ -0,0 +1,30 @@
+package sample.rmi;
+
+import java.awt.*;
+import java.awt.event.*;
+
+public class AlertDialog extends Frame implements ActionListener {
+ private Label label;
+
+ public AlertDialog() {
+ super("Alert");
+ setSize(200, 100);
+ setLocation(100, 100);
+ label = new Label();
+ Button b = new Button("OK");
+ b.addActionListener(this);
+ Panel p = new Panel();
+ p.add(b);
+ add("North", label);
+ add("South", p);
+ }
+
+ public void show(String message) {
+ label.setText(message);
+ setVisible(true);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ setVisible(false);
+ }
+}
diff --git a/sample/rmi/CountApplet.java b/sample/rmi/CountApplet.java
new file mode 100644
index 00000000..0bebdaf9
--- /dev/null
+++ b/sample/rmi/CountApplet.java
@@ -0,0 +1,83 @@
+package sample.rmi;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+import javassist.rmi.ObjectImporter;
+import javassist.rmi.ObjectNotFoundException;
+import javassist.web.Viewer;
+
+public class CountApplet extends Applet implements ActionListener {
+ private Font font;
+ private ObjectImporter importer;
+ private Counter counter;
+ private AlertDialog dialog;
+ private String message;
+
+ private String paramButton;
+ private String paramName;
+
+ public void init() {
+ paramButton = getParameter("button");
+ paramName = getParameter("name");
+ importer = new ObjectImporter(this);
+ commonInit();
+ }
+
+ /* call this method instead of init() if this program is not run
+ * as an applet.
+ */
+ public void applicationInit() {
+ paramButton = "OK";
+ paramName = "counter";
+ Viewer cl = (Viewer)getClass().getClassLoader();
+ importer = new ObjectImporter(cl.getServer(), cl.getPort());
+ commonInit();
+ }
+
+ private void commonInit() {
+ font = new Font("SansSerif", Font.ITALIC, 40);
+ Button b = new Button(paramButton);
+ b.addActionListener(this);
+ add(b);
+ dialog = new AlertDialog();
+ message = "???";
+ }
+
+ public void destroy() {
+ dialog.dispose();
+ }
+
+ public void start() {
+ try {
+ counter = (Counter)importer.lookupObject(paramName);
+ message = Integer.toString(counter.get());
+ }
+ catch (ObjectNotFoundException e) {
+ dialog.show(e.toString());
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ counter.increase();
+ message = Integer.toString(counter.get());
+ repaint();
+ }
+
+ public void paint(Graphics g) {
+ g.setFont(font);
+ g.drawRect(50, 50, 100, 100);
+ g.setColor(Color.blue);
+ g.drawString(message, 60, 120);
+ }
+
+ public static void main(String[] args) {
+ Frame f = new Frame("CountApplet");
+ CountApplet ca = new CountApplet();
+ f.add(ca);
+ f.setSize(300, 300);
+ ca.applicationInit();
+ ca.start();
+ f.setVisible(true);
+ }
+}
diff --git a/sample/rmi/Counter.java b/sample/rmi/Counter.java
new file mode 100644
index 00000000..f8a0fcf5
--- /dev/null
+++ b/sample/rmi/Counter.java
@@ -0,0 +1,32 @@
+package sample.rmi;
+
+import javassist.rmi.AppletServer;
+import java.io.IOException;
+import javassist.CannotCompileException;
+import javassist.NotFoundException;
+
+public class Counter {
+ private int count = 0;
+
+ public int get() {
+ return count;
+ }
+
+ synchronized public int increase() {
+ count += 1;
+ return count;
+ }
+
+ public static void main(String[] args)
+ throws IOException, NotFoundException, CannotCompileException
+ {
+ if (args.length == 1) {
+ AppletServer web = new AppletServer(args[0]);
+ web.exportObject("counter", new Counter());
+ web.run();
+ }
+ else
+ System.err.println(
+ "Usage: java sample.rmi.Counter <port number>");
+ }
+}
diff --git a/sample/rmi/inside.gif b/sample/rmi/inside.gif
new file mode 100644
index 00000000..c69c8ee8
--- /dev/null
+++ b/sample/rmi/inside.gif
Binary files differ
diff --git a/sample/rmi/start.html b/sample/rmi/start.html
new file mode 100644
index 00000000..696b629c
--- /dev/null
+++ b/sample/rmi/start.html
@@ -0,0 +1,15 @@
+<h2>Instructions</h2>
+
+<p>1. Run the server on the local host (where your web browser is running):
+
+<ul>% java sample.rmi.Counter 5001</ul>
+
+<p>2. Click below:
+
+<h2><a href="webdemo.html">
+Start!
+</a></h2>
+
+<p>If you don't want to use a web browser, do as follows:
+
+<ul><pre>% java javassist.web.Viewer localhost 5001 sample.rmi.CountApplet</pre></ul>
diff --git a/sample/rmi/webdemo.html b/sample/rmi/webdemo.html
new file mode 100644
index 00000000..d313ec3c
--- /dev/null
+++ b/sample/rmi/webdemo.html
@@ -0,0 +1,203 @@
+<html>
+<body>
+<h2>Remote Method Invocation</h2>
+
+<P>Javassist enables an applet to access a remote object as if it is a
+local object. The applet can communicate through a socket with the
+host that executes the web server distributing that applet. However,
+the applet cannot directly call a method on an object if the object is
+on a remote host. The <code>javassist.rmi</code> package provides
+a mechanism for the applet to transparently access the remote object.
+The rules that the applet must be subject to are simpler than the
+standard Java RMI.
+
+<h3>1. Sample applet</h3>
+
+<P>The applet showing below is a simple number counter.
+If you press the button, the number is increased by one.
+An interesting feature of this applet is that the object
+recording the current number is contained by the web server
+written in Java. The applet must access the object through a socket
+to obtain the current number.
+
+<p><center>
+<applet codebase="http://localhost:5001"
+code="sample.rmi.CountApplet" width=200 height=200>
+<param name=name value="counter">
+<param name=button value="+1">
+</applet>
+</center>
+
+<p>However, the program of the applet does not need to directly handle
+a socket. The <code>ObjectImporter</code> provided by Javassist deals
+with all the awkward programming.
+Look at the lines shown with red:
+
+<p><b>Figure 1: Applet</b>
+
+<pre>
+<font color="red">import javassist.rmi.ObjectImporter;</font>
+
+public class CountApplet extends Applet implements ActionListener {
+ private Font font;
+ <font color="red">private ObjectImporter importer;
+ private Counter counter;</font>
+ private AlertDialog dialog;
+ private String message;
+
+ public void init() {
+ font = new Font("SansSerif", Font.ITALIC, 40);
+ Button b = new Button(getParameter("button"));
+ b.addActionListener(this);
+ add(b);
+ <font color="red">importer = new ObjectImporter(this);</font>
+ dialog = new AlertDialog();
+ message = "???";
+ }
+
+ public void start() {
+ String counterName = getParameter("name");
+ <font color="red">counter = (Counter)importer.getObject(counterName);</font>
+ message = Integer.toString(<font color="red">counter.get()</font>);
+ }
+
+ /* The method called when the button is pressed.
+ */
+ public void actionPerformed(ActionEvent e) {
+ message = Integer.toString(<font color="red">counter.increase()</font>);
+ repaint();
+ }
+
+ public void paint(Graphics g) {
+ g.setFont(font);
+ g.drawRect(50, 50, 100, 100);
+ g.setColor(Color.blue);
+ g.drawString(message, 60, 120);
+ }
+}
+</pre>
+
+<p>A <code>Counter</code> object running on a remote host
+maintains the counter number. To access this object, the applet first
+calls <code>getObject()</code> on an <code>ObjectImporter</code>
+to obtain a reference to the object. The parameter is the name associated
+with the object by the web server. Once the reference is obtained, it
+is delt with as if it is a reference to a local object.
+For example, <code>counter.get()</code> and <code>counter.increase()</code>
+call methods on the remote object.
+
+<p>The definition of the <code>Counter</code> class is also
+straightforward:
+
+<p><b>Figure 2: Remote object</b>
+
+<pre>
+public class Counter {
+ private int count = 0;
+
+ public int get() {
+ return count;
+ }
+
+ public int increase() {
+ count += 1;
+ return count;
+ }
+}
+</pre>
+
+<p>Note that the <code>javassist.rmi</code> package does not require
+the <code>Counter</code> class to be an interface unlike the Java RMI,
+with which <code>Counter</code> must be an interface and it must be
+implemented by another class.
+
+<p>To make the <code>Counter</code> object available from the applet,
+it must be registered with the web server. A <code>AppletServer</code>
+object is a simple webserver that can distribute <code>.html</code> files
+and <code>.class</code> files (Java applets).
+
+<p><b>Figure 3: Server-side program</b>
+
+<pre>
+public class MyWebServer {
+ public static void main(String[] args) throws IOException, CannotCompileException
+ {
+ AppletServer web = new AppletServer(args[0]);
+ <font color="red">web.exportObject("counter", new Counter());</font>
+ web.run();
+ }
+}
+</pre>
+
+<p>The <code>exportObject()</code> method registers a remote object
+with the <code>AppletServer</code> object. In the example above,
+a <code>Counter</code> object is registered. The applet can access
+the object with the name "counter". The web server starts the service
+if the <code>run()</code> method is called.
+
+<p><br>
+
+<h3>2. Features</h3>
+
+The remote method invocation mechanism provided by Javassist has the
+following features:
+
+<ul>
+<li><b>Regular Java syntax:</b><br>
+ The applet can call a method on a remote object with regular
+ Java syntax.
+<p>
+
+<li><b>No special naming convention:</b><br>
+ The applet can use the same class name as the server-side program.
+ The reference object to a remote <code>Foo</code> object is
+ also represented by the class <code>Foo</code>.
+ Unlike other similar
+ systems, it is not represented by a different class such as
+ <code>ProxyFoo</code> or an interface implemented by
+ <code>Foo</code>.
+<p>
+
+<li><b>No extra compiler:</b><br>
+ All the programs, both the applet and the server-side program,
+ are compiled by the regular Java compiler. No external compiler
+ is needed.
+</ul>
+
+<p> With the Java RMI or Voyager, the applet programmer must define
+an interface for every remote object class and access the remote object
+through that interface.
+On the other hand, the <code>javassist.rmi</code> package does not
+require the programmer to follow that programming convention.
+It is suitable for writing simple distributed programs like applets.
+
+<p><br>
+
+<h3>3. Inside of the system</h3>
+
+<p>A key idea of the implementation is that the applet and the server-side
+program must use different versions of the class <code>Counter</code>.
+The <code>Counter</code> object in the applet must work as a proxy
+object, which transfers the method invocations to the <code>Counter</code>
+object in the server-side program.
+
+<p>With other systems like the Java RMI, the class of this proxy object is
+produced by a special compiler such as <code>rmic</code>.
+It must be manually maintained by the programmer.
+
+<center><img src="inside.gif"></center>
+
+<p>However, Javassist automatically generates the proxy class at
+runtime so that the programmer does not have to be concerned about the
+maintenance of the proxy class.
+If the web browser running the applet
+requests to load the <code>Counter</code> class, which is the class
+of an exported object,
+then the web server
+transfers the version of <code>Counter</code> that Javassist generates
+as a proxy class.
+
+<p><br>
+
+</body>
+</html>
diff --git a/sample/vector/Sample.java b/sample/vector/Sample.java
new file mode 100644
index 00000000..7a47aadf
--- /dev/null
+++ b/sample/vector/Sample.java
@@ -0,0 +1,14 @@
+package sample.vector;
+
+public class Sample extends java.util.Vector {
+ public void add(X e) {
+ super.addElement(e);
+ }
+
+ public X at(int i) {
+ return (X)super.elementAt(i);
+ }
+}
+
+class X {
+}
diff --git a/sample/vector/Sample2.java b/sample/vector/Sample2.java
new file mode 100644
index 00000000..dd5c965e
--- /dev/null
+++ b/sample/vector/Sample2.java
@@ -0,0 +1,13 @@
+package sample.vector;
+
+public class Sample2 extends java.util.Vector {
+ public Object add(Object[] args) {
+ super.addElement(args[0]);
+ return null;
+ }
+
+ public Object at(Object[] args) {
+ int i = ((Integer)args[0]).intValue();
+ return super.elementAt(i);
+ }
+}
diff --git a/sample/vector/Test.j b/sample/vector/Test.j
new file mode 100644
index 00000000..6f524c93
--- /dev/null
+++ b/sample/vector/Test.j
@@ -0,0 +1,38 @@
+/*
+ A sample program using sample.vector.VectorAssistant
+ and the javassist.preproc package.
+
+ This automatically produces the classes representing vectors of integer
+ and vectors of java.lang.String.
+
+ To compile and run this program, do as follows:
+
+ % java javassist.tool.Compiler sample/vector/Test.j
+ % javac sample/vector/Test.java
+ % java sample.vector.Test
+
+ The first line produces one source file (sample/Test.java) and
+ two class files (sample/vector/intVector.class and
+ sample/vector/StringVector.class).
+*/
+
+package sample.vector;
+
+import java.util.Vector by sample.vector.VectorAssistant(java.lang.String);
+import java.util.Vector by sample.vector.VectorAssistant(int);
+
+public class Test {
+ public static void main(String[] args) {
+ intVector iv = new intVector();
+ iv.add(3);
+ iv.add(4);
+ for (int i = 0; i < iv.size(); ++i)
+ System.out.println(iv.at(i));
+
+ StringVector sv = new StringVector();
+ sv.add("foo");
+ sv.add("bar");
+ for (int i = 0; i < sv.size(); ++i)
+ System.out.println(sv.at(i));
+ }
+}
diff --git a/sample/vector/VectorAssistant.java b/sample/vector/VectorAssistant.java
new file mode 100644
index 00000000..44fdd41c
--- /dev/null
+++ b/sample/vector/VectorAssistant.java
@@ -0,0 +1,135 @@
+package sample.vector;
+
+import java.io.IOException;
+import javassist.*;
+import javassist.preproc.Assistant;
+
+/**
+ * This is a Javassist program which produce a new class representing
+ * vectors of a given type. For example,
+ *
+ * <ul>import java.util.Vector by sample.vector.VectorAssistant(int)</ul>
+ *
+ * <p>requests the Javassist preprocessor to substitute the following
+ * lines for the original import declaration:
+ *
+ * <ul><pre>
+ * import java.util.Vector;
+ * import sample.vector.intVector;
+ * </pre></ul>
+ *
+ * <p>The Javassist preprocessor calls <code>VectorAssistant.assist()</code>
+ * and produces class <code>intVector</code> equivalent to:
+ *
+ * <ul><pre>
+ * package sample.vector;
+ *
+ * public class intVector extends Vector {
+ * pubilc void add(int value) {
+ * addElement(new Integer(value));
+ * }
+ *
+ * public int at(int index) {
+ * return elementAt(index).intValue();
+ * }
+ * }
+ * </pre></ul>
+ *
+ * <p><code>VectorAssistant.assist()</code> uses
+ * <code>sample.vector.Sample</code> and <code>sample.vector.Sample2</code>
+ * as a template to produce the methods <code>add()</code> and
+ * <code>at()</code>.
+ */
+public class VectorAssistant implements Assistant {
+ public final String packageName = "sample.vector.";
+
+ /**
+ * Calls <code>makeSubclass()</code> and produces a new vector class.
+ * This method is called by a <code>javassist.preproc.Compiler</code>.
+ *
+ * @see javassist.preproc.Compiler
+ */
+ public CtClass[] assist(ClassPool pool, String vec, String[] args)
+ throws CannotCompileException
+ {
+ if (args.length != 1)
+ throw new CannotCompileException(
+ "VectorAssistant receives a single argument.");
+
+ try {
+ CtClass subclass;
+ CtClass elementType = pool.get(args[0]);
+ if (elementType.isPrimitive())
+ subclass = makeSubclass2(pool, elementType);
+ else
+ subclass = makeSubclass(pool, elementType);
+
+ CtClass[] results = { subclass, pool.get(vec) };
+ return results;
+ }
+ catch (NotFoundException e) {
+ throw new CannotCompileException(e);
+ }
+ catch (IOException e) {
+ throw new CannotCompileException(e);
+ }
+ }
+
+ /**
+ * Produces a new vector class. This method does not work if
+ * the element type is a primitive type.
+ *
+ * @param type the type of elements
+ */
+ public CtClass makeSubclass(ClassPool pool, CtClass type)
+ throws CannotCompileException, NotFoundException, IOException
+ {
+ CtClass vec = pool.makeClass(makeClassName(type));
+ vec.setSuperclass(pool.get("java.util.Vector"));
+
+ CtClass c = pool.get("sample.vector.Sample");
+ CtMethod addmethod = c.getDeclaredMethod("add");
+ CtMethod atmethod = c.getDeclaredMethod("at");
+
+ ClassMap map = new ClassMap();
+ map.put("sample.vector.X", type.getName());
+
+ vec.addMethod(CtNewMethod.copy(addmethod, "add", vec, map));
+ vec.addMethod(CtNewMethod.copy(atmethod, "at", vec, map));
+ pool.writeFile(vec.getName());
+ return vec;
+ }
+
+ /**
+ * Produces a new vector class. This uses wrapped methods so that
+ * the element type can be a primitive type.
+ *
+ * @param type the type of elements
+ */
+ public CtClass makeSubclass2(ClassPool pool, CtClass type)
+ throws CannotCompileException, NotFoundException, IOException
+ {
+ CtClass vec = pool.makeClass(makeClassName(type));
+ vec.setSuperclass(pool.get("java.util.Vector"));
+
+ CtClass c = pool.get("sample.vector.Sample2");
+ CtMethod addmethod = c.getDeclaredMethod("add");
+ CtMethod atmethod = c.getDeclaredMethod("at");
+
+ CtClass[] args1 = { type };
+ CtClass[] args2 = { CtClass.intType };
+ CtMethod m
+ = CtNewMethod.wrapped(CtClass.voidType, "add", args1,
+ null, addmethod, null, vec);
+ vec.addMethod(m);
+ m = CtNewMethod.wrapped(type, "at", args2,
+ null, atmethod, null, vec);
+ vec.addMethod(m);
+ pool.writeFile(vec.getName());
+ return vec;
+ }
+
+ private String makeClassName(CtClass type) {
+ return packageName + type.getSimpleName() + "Vector";
+ }
+}
diff --git a/src/main/META-INF/MANIFEST.MF b/src/main/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..b28ad33a
--- /dev/null
+++ b/src/main/META-INF/MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Specification-Title: Javassist
+Created-By: Shigeru Chiba, Tokyo Institute of Technology
+Specification-Vendor: Shigeru Chiba, Tokyo Institute of Technology
+Specification-Version: 2.4
+Name: javassist/
+
diff --git a/src/main/javassist/ByteArrayClassPath.java b/src/main/javassist/ByteArrayClassPath.java
new file mode 100644
index 00000000..14b37d8f
--- /dev/null
+++ b/src/main/javassist/ByteArrayClassPath.java
@@ -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
index 00000000..41ec7ff5
--- /dev/null
+++ b/src/main/javassist/CannotCompileException.java
@@ -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
index 00000000..c729ff7c
--- /dev/null
+++ b/src/main/javassist/ClassMap.java
@@ -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
index 00000000..f50c225c
--- /dev/null
+++ b/src/main/javassist/ClassPath.java
@@ -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
index 00000000..33907446
--- /dev/null
+++ b/src/main/javassist/ClassPool.java
@@ -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
index 00000000..1c6c7058
--- /dev/null
+++ b/src/main/javassist/ClassPoolTail.java
@@ -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
index 00000000..051699b4
--- /dev/null
+++ b/src/main/javassist/CodeConverter.java
@@ -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
index 00000000..904f15c9
--- /dev/null
+++ b/src/main/javassist/CtArray.java
@@ -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
index 00000000..220612b7
--- /dev/null
+++ b/src/main/javassist/CtBehavior.java
@@ -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
index 00000000..1d70b8bf
--- /dev/null
+++ b/src/main/javassist/CtClass.java
@@ -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
index 00000000..e08ed7bd
--- /dev/null
+++ b/src/main/javassist/CtClassType.java
@@ -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
index 00000000..52024e77
--- /dev/null
+++ b/src/main/javassist/CtConstructor.java
@@ -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
index 00000000..6bb45e9c
--- /dev/null
+++ b/src/main/javassist/CtField.java
@@ -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
index 00000000..8621a18b
--- /dev/null
+++ b/src/main/javassist/CtMember.java
@@ -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
index 00000000..dbe1931c
--- /dev/null
+++ b/src/main/javassist/CtMethod.java
@@ -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
index 00000000..09bf345d
--- /dev/null
+++ b/src/main/javassist/CtNewClass.java
@@ -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
index 00000000..13bae8a6
--- /dev/null
+++ b/src/main/javassist/CtNewConstructor.java
@@ -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
index 00000000..05532902
--- /dev/null
+++ b/src/main/javassist/CtNewMethod.java
@@ -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
index 00000000..fa4786e9
--- /dev/null
+++ b/src/main/javassist/CtNewWrappedConstructor.java
@@ -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
index 00000000..718e3c4f
--- /dev/null
+++ b/src/main/javassist/CtNewWrappedMethod.java
@@ -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
index 00000000..8e73ca8e
--- /dev/null
+++ b/src/main/javassist/CtPrimitiveType.java
@@ -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
index 00000000..afc91066
--- /dev/null
+++ b/src/main/javassist/Dump.java
@@ -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
index 00000000..948d3de9
--- /dev/null
+++ b/src/main/javassist/Loader.java
@@ -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
index 00000000..e6c7d2c5
--- /dev/null
+++ b/src/main/javassist/Modifier.java
@@ -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
index 00000000..30b4b882
--- /dev/null
+++ b/src/main/javassist/NotFoundException.java
@@ -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
index 00000000..a16009ca
--- /dev/null
+++ b/src/main/javassist/SerialVersionUID.java
@@ -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
index 00000000..42599c9d
--- /dev/null
+++ b/src/main/javassist/Translator.java
@@ -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
index 00000000..c359c778
--- /dev/null
+++ b/src/main/javassist/URLClassPath.java
@@ -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
index 00000000..c964ca7c
--- /dev/null
+++ b/src/main/javassist/bytecode/AccessFlag.java
@@ -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
index 00000000..29d7904b
--- /dev/null
+++ b/src/main/javassist/bytecode/AttributeInfo.java
@@ -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
index 00000000..51d287d7
--- /dev/null
+++ b/src/main/javassist/bytecode/BadBytecode.java
@@ -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
index 00000000..e10231e8
--- /dev/null
+++ b/src/main/javassist/bytecode/ByteArray.java
@@ -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
index 00000000..f903e4be
--- /dev/null
+++ b/src/main/javassist/bytecode/Bytecode.java
@@ -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
index 00000000..063caa59
--- /dev/null
+++ b/src/main/javassist/bytecode/ClassFile.java
@@ -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
index 00000000..5b83a5aa
--- /dev/null
+++ b/src/main/javassist/bytecode/ClassFileWriter.java
@@ -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
index 00000000..6f45c03e
--- /dev/null
+++ b/src/main/javassist/bytecode/CodeAttribute.java
@@ -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
index 00000000..09735e27
--- /dev/null
+++ b/src/main/javassist/bytecode/CodeIterator.java
@@ -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
index 00000000..5d079e3a
--- /dev/null
+++ b/src/main/javassist/bytecode/ConstPool.java
@@ -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
index 00000000..4d913e15
--- /dev/null
+++ b/src/main/javassist/bytecode/ConstantAttribute.java
@@ -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
index 00000000..065fbccf
--- /dev/null
+++ b/src/main/javassist/bytecode/Descriptor.java
@@ -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
index 00000000..71baa3b5
--- /dev/null
+++ b/src/main/javassist/bytecode/ExceptionTable.java
@@ -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
index 00000000..db304e71
--- /dev/null
+++ b/src/main/javassist/bytecode/ExceptionsAttribute.java
@@ -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
index 00000000..d5e3591c
--- /dev/null
+++ b/src/main/javassist/bytecode/FieldInfo.java
@@ -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
index 00000000..954a166b
--- /dev/null
+++ b/src/main/javassist/bytecode/InnerClassesAttribute.java
@@ -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
index 00000000..35f1a020
--- /dev/null
+++ b/src/main/javassist/bytecode/LineNumberAttribute.java
@@ -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
index 00000000..86ba7938
--- /dev/null
+++ b/src/main/javassist/bytecode/LongVector.java
@@ -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
index 00000000..567529a3
--- /dev/null
+++ b/src/main/javassist/bytecode/MethodInfo.java
@@ -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
index 00000000..5fa4fd5c
--- /dev/null
+++ b/src/main/javassist/bytecode/Mnemonic.java
@@ -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
index 00000000..e0c21b3c
--- /dev/null
+++ b/src/main/javassist/bytecode/Opcode.java
@@ -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
index 00000000..22e6f6f9
--- /dev/null
+++ b/src/main/javassist/bytecode/SourceFileAttribute.java
@@ -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
index 00000000..63d559ce
--- /dev/null
+++ b/src/main/javassist/bytecode/SyntheticAttribute.java
@@ -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
index 00000000..9a5f6771
--- /dev/null
+++ b/src/main/javassist/compiler/CodeGen.java
@@ -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
index 00000000..98b97ef5
--- /dev/null
+++ b/src/main/javassist/compiler/CompileError.java
@@ -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
index 00000000..8f15ca81
--- /dev/null
+++ b/src/main/javassist/compiler/Javac.java
@@ -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
index 00000000..6a365e16
--- /dev/null
+++ b/src/main/javassist/compiler/JvstCodeGen.java
@@ -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
index 00000000..57145bf8
--- /dev/null
+++ b/src/main/javassist/compiler/KeywordTable.java
@@ -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
index 00000000..fb188de5
--- /dev/null
+++ b/src/main/javassist/compiler/Lex.java
@@ -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
index 00000000..d457a836
--- /dev/null
+++ b/src/main/javassist/compiler/MemberCodeGen.java
@@ -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
index 00000000..a8102863
--- /dev/null
+++ b/src/main/javassist/compiler/NoFieldException.java
@@ -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
index 00000000..3039e5d1
--- /dev/null
+++ b/src/main/javassist/compiler/Parser.java
@@ -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
index 00000000..a787223e
--- /dev/null
+++ b/src/main/javassist/compiler/ProceedHandler.java
@@ -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
index 00000000..be318811
--- /dev/null
+++ b/src/main/javassist/compiler/SymbolTable.java
@@ -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
index 00000000..f4c06d8f
--- /dev/null
+++ b/src/main/javassist/compiler/SyntaxError.java
@@ -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
index 00000000..8b78bf00
--- /dev/null
+++ b/src/main/javassist/compiler/TokenId.java
@@ -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
index 00000000..887a528a
--- /dev/null
+++ b/src/main/javassist/compiler/ast/ASTList.java
@@ -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
index 00000000..72825142
--- /dev/null
+++ b/src/main/javassist/compiler/ast/ASTree.java
@@ -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
index 00000000..0a062d46
--- /dev/null
+++ b/src/main/javassist/compiler/ast/AssignExpr.java
@@ -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
index 00000000..a8fdeb62
--- /dev/null
+++ b/src/main/javassist/compiler/ast/BinExpr.java
@@ -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
index 00000000..7f8775cb
--- /dev/null
+++ b/src/main/javassist/compiler/ast/CastExpr.java
@@ -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
index 00000000..d4a8a108
--- /dev/null
+++ b/src/main/javassist/compiler/ast/CondExpr.java
@@ -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
index 00000000..edeb839d
--- /dev/null
+++ b/src/main/javassist/compiler/ast/Declarator.java
@@ -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
index 00000000..459f030f
--- /dev/null
+++ b/src/main/javassist/compiler/ast/DoubleConst.java
@@ -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
index 00000000..1fecc9ce
--- /dev/null
+++ b/src/main/javassist/compiler/ast/Expr.java
@@ -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
index 00000000..d57dd632
--- /dev/null
+++ b/src/main/javassist/compiler/ast/FieldDecl.java
@@ -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
index 00000000..03d6bc1f
--- /dev/null
+++ b/src/main/javassist/compiler/ast/InstanceOfExpr.java
@@ -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
index 00000000..63ce70cb
--- /dev/null
+++ b/src/main/javassist/compiler/ast/IntConst.java
@@ -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
index 00000000..9e1798ec
--- /dev/null
+++ b/src/main/javassist/compiler/ast/Keyword.java
@@ -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
index 00000000..a4825874
--- /dev/null
+++ b/src/main/javassist/compiler/ast/Member.java
@@ -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
index 00000000..3c529014
--- /dev/null
+++ b/src/main/javassist/compiler/ast/MethodDecl.java
@@ -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
index 00000000..33c721ed
--- /dev/null
+++ b/src/main/javassist/compiler/ast/NewExpr.java
@@ -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
index 00000000..520207a8
--- /dev/null
+++ b/src/main/javassist/compiler/ast/Pair.java
@@ -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
index 00000000..c6b9a97e
--- /dev/null
+++ b/src/main/javassist/compiler/ast/Stmnt.java
@@ -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
index 00000000..5f173f50
--- /dev/null
+++ b/src/main/javassist/compiler/ast/StringL.java
@@ -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
index 00000000..c8e753bf
--- /dev/null
+++ b/src/main/javassist/compiler/ast/Symbol.java
@@ -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
index 00000000..16112a99
--- /dev/null
+++ b/src/main/javassist/compiler/ast/Variable.java
@@ -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
index 00000000..685713de
--- /dev/null
+++ b/src/main/javassist/compiler/ast/Visitor.java
@@ -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
index 00000000..e9704348
--- /dev/null
+++ b/src/main/javassist/convert/TransformAfter.java
@@ -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
index 00000000..79deb10f
--- /dev/null
+++ b/src/main/javassist/convert/TransformBefore.java
@@ -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
index 00000000..985a8bbb
--- /dev/null
+++ b/src/main/javassist/convert/TransformCall.java
@@ -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
index 00000000..bce010da
--- /dev/null
+++ b/src/main/javassist/convert/TransformFieldAccess.java
@@ -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
index 00000000..a69918d8
--- /dev/null
+++ b/src/main/javassist/convert/TransformNew.java
@@ -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
index 00000000..fda192c1
--- /dev/null
+++ b/src/main/javassist/convert/TransformReadField.java
@@ -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
index 00000000..8c37c7fd
--- /dev/null
+++ b/src/main/javassist/convert/TransformWriteField.java
@@ -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
index 00000000..f1a4b566
--- /dev/null
+++ b/src/main/javassist/convert/Transformer.java
@@ -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
index 00000000..44648e78
--- /dev/null
+++ b/src/main/javassist/expr/Cast.java
@@ -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
index 00000000..d88d7939
--- /dev/null
+++ b/src/main/javassist/expr/Expr.java
@@ -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
index 00000000..c6814ef5
--- /dev/null
+++ b/src/main/javassist/expr/ExprEditor.java
@@ -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
index 00000000..a524b07d
--- /dev/null
+++ b/src/main/javassist/expr/FieldAccess.java
@@ -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
index 00000000..5b980f8e
--- /dev/null
+++ b/src/main/javassist/expr/Instanceof.java
@@ -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
index 00000000..747380b4
--- /dev/null
+++ b/src/main/javassist/expr/MethodCall.java
@@ -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
index 00000000..73546238
--- /dev/null
+++ b/src/main/javassist/expr/NewExpr.java
@@ -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
index 00000000..60d7c4c2
--- /dev/null
+++ b/src/main/javassist/preproc/Assistant.java
@@ -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
index 00000000..60e6a9d7
--- /dev/null
+++ b/src/main/javassist/preproc/Compiler.java
@@ -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
index 00000000..168032fb
--- /dev/null
+++ b/src/main/javassist/reflect/CannotCreateException.java
@@ -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
index 00000000..455c3e4d
--- /dev/null
+++ b/src/main/javassist/reflect/CannotInvokeException.java
@@ -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
index 00000000..6c0de555
--- /dev/null
+++ b/src/main/javassist/reflect/ClassMetaobject.java
@@ -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
index 00000000..6e0b854e
--- /dev/null
+++ b/src/main/javassist/reflect/Compiler.java
@@ -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
index 00000000..5287593d
--- /dev/null
+++ b/src/main/javassist/reflect/Loader.java
@@ -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
index 00000000..96e53d52
--- /dev/null
+++ b/src/main/javassist/reflect/Metalevel.java
@@ -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
index 00000000..8f1d02bc
--- /dev/null
+++ b/src/main/javassist/reflect/Metaobject.java
@@ -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
index 00000000..f76b396e
--- /dev/null
+++ b/src/main/javassist/reflect/Reflection.java
@@ -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
index 00000000..0b760de2
--- /dev/null
+++ b/src/main/javassist/reflect/Sample.java
@@ -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
index 00000000..2b8c30e5
--- /dev/null
+++ b/src/main/javassist/rmi/AppletServer.java
@@ -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
index 00000000..19b6cb4e
--- /dev/null
+++ b/src/main/javassist/rmi/ObjectImporter.java
@@ -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
index 00000000..e31db370
--- /dev/null
+++ b/src/main/javassist/rmi/ObjectNotFoundException.java
@@ -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
index 00000000..f310d358
--- /dev/null
+++ b/src/main/javassist/rmi/Proxy.java
@@ -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
index 00000000..6b89c64b
--- /dev/null
+++ b/src/main/javassist/rmi/RemoteException.java
@@ -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
index 00000000..0017ecc1
--- /dev/null
+++ b/src/main/javassist/rmi/RemoteRef.java
@@ -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
index 00000000..fee85d27
--- /dev/null
+++ b/src/main/javassist/rmi/Sample.java
@@ -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
index 00000000..9e77c383
--- /dev/null
+++ b/src/main/javassist/rmi/StubGenerator.java
@@ -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
index 00000000..693da245
--- /dev/null
+++ b/src/main/javassist/runtime/Cflow.java
@@ -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
index 00000000..0bf49bf2
--- /dev/null
+++ b/src/main/javassist/runtime/Desc.java
@@ -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
index 00000000..aafebce0
--- /dev/null
+++ b/src/main/javassist/web/BadHttpRequest.java
@@ -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
index 00000000..ce57c016
--- /dev/null
+++ b/src/main/javassist/web/Viewer.java
@@ -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
index 00000000..95ad05a2
--- /dev/null
+++ b/src/main/javassist/web/Webserver.java
@@ -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
index 00000000..b88ddb46
--- /dev/null
+++ b/tutorial/brown.css
@@ -0,0 +1,19 @@
+h1,h2,h3 {
+ color:#663300;
+ padding:4px 6px 6px 10px;
+ border-width:1px 0px 1px 0px;
+ border-color:#F5DEB3;
+ border-style:solid;
+}
+
+h3 {
+ padding-left: 30px;
+}
+
+h4 {
+ color:#663300;
+}
+
+em {
+ color:#cc0000;
+}
diff --git a/tutorial/overview.gif b/tutorial/overview.gif
new file mode 100644
index 00000000..953c59a6
--- /dev/null
+++ b/tutorial/overview.gif
Binary files differ
diff --git a/tutorial/sequence.gif b/tutorial/sequence.gif
new file mode 100644
index 00000000..a59d66fd
--- /dev/null
+++ b/tutorial/sequence.gif
Binary files differ
diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html
new file mode 100644
index 00000000..3fac03ee
--- /dev/null
+++ b/tutorial/tutorial.html
@@ -0,0 +1,560 @@
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Javassist Tutorial</title>
+ <link rel="stylesheet" type="text/css" href="brown.css">
+</head>
+<body>
+
+<b>
+<font size="+3">
+Getting Started with Javassist
+</font>
+
+<p><font size="+2">
+Shigeru Chiba
+</font>
+</b>
+
+<p><div align="right"><a href="tutorial2.html">Next page</a></div>
+
+<ul>1. <a href="#read">Reading bytecode</a>
+<br>2. <a href="#def">Defining a new class</a>
+<br>3. <a href="#mod">Modifying a class at load time</a>
+<br>4. <a href="#load">Class loader</a>
+<br>5. <a href="tutorial2.html#intro">Introspection and customization</a>
+</ul>
+
+<p><br>
+
+<a name="read">
+<h2>1. Reading bytecode</h2>
+
+<p>Javassist is a class library for dealing with Java bytecode.
+Java bytecode is stored in a binary file called a class file.
+Each class file contains one Java class or interface.
+
+<p>The class <code>Javassist.CtClass</code> is an abstract representation
+of a class file. A <code>CtClass</code> object is a handle for dealing
+with a class file. The following program is a very simple example:
+
+<ul><pre>
+ClassPool pool = ClassPool.getDefault();
+CtClass cc = pool.get("test.Rectangle");
+cc.setSuperclass(pool.get("test.Point"));
+pool.writeFile("test.Rectangle"); // or simply, cc.writeFile()
+</pre></ul>
+
+<p>This program first obtains a <code>ClassPool</code> object,
+which controls bytecode modification with Javassist.
+The <code>ClassPool</code> object is a container of <code>CtClass</code>
+object representing a class file.
+It reads a class file on demand for constructing a <code>CtClass</code>
+object and contains the constructed object until it is written out
+to a file or an output stream.
+
+<p>To modify the definition of a class, the users must first obtain a
+reference to the <code>CtClass</code> object representing that class.
+<code>ClassPool.get()</code> is used for this purpose.
+In the case of the program above, the <code>CtClass</code> object
+representing a class <code>test.Rectangle</code> is obtained from
+the <code>ClassPool</code> object
+and it is assigned
+to a variable <code>cc</code>. Then it is modified so that
+the superclass of <code>test.Rectangle</code> is changed into
+a class <code>test.Point</code>.
+This change is reflected on the original class file when
+<code>ClassPool.writeFile()</code> is finally called.
+
+<p>Note that <code>writeFile()</code> is a method declared in not
+<code>CtClass</code> but <code>ClassPool</code>.
+If this method is called, the <code>ClassPool</code>
+finds a <code>CtClass</code> object specified with a class name
+among the objects that the <code>ClassPool</code> contains.
+Then it translates that <code>CtClass</code> object into a class file
+and writes it on a local disk.
+
+<p>There is also <code>writeFile()</code> defined in <code>CtClass</code>.
+Thus, the last line in the program above can be rewritten into:
+
+<ul><pre>cc.writeFile();</pre></ul>
+
+<p>This method is a convenient method for invoking <code>writeFile()</code>
+in <code>ClassPool</code> with the name of the class represented by
+<code>cc</code>.
+
+<p>Javassist also provides a method for directly obtaining the
+modified bytecode. To do this, call <code>write()</code>:
+
+<ul><pre>
+byte[] b = pool.write("test.Rectangle");
+</pre></ul>
+
+<p>The contents of the class file for <code>test.Rectangle</code> are
+assigned to a variable <code>b</code> in the form of byte array.
+<code>writeFile()</code> also internally calls <code>write()</code>
+to obtain the byte array written in a class file.
+
+<p>The default <code>ClassPool</code> returned
+by a static method <code>ClassPool.getDefault()</code>
+searches the same path as the underlying JVM.
+The users can expand this class search path if needed.
+For example, the following code adds a directory
+<code>/usr/local/javalib</code>
+to the search path:
+
+<ul><pre>
+ClassPool pool = ClassPool.getDefault();
+pool.insertClassPath("/usr/local/javalib");
+</pre></ul>
+
+<p>The search path that the users can add is not only a directory but also
+a URL:
+
+<ul><pre>
+ClassPool pool = ClassPool.getDefault();
+ClassPath cp = new URLClassPath("www.foo.com", 80, "/java/", "com.foo.");
+pool.insertClassPath(cp);
+</pre></ul>
+
+<p>This program adds "http://www.foo.com:80/java/" to the class search
+path. This URL is used only for searching classes belonging to a
+package <code>com.foo</code>.
+
+<p>You can directly give a byte array to a <code>ClassPool</code> object
+and construct a <code>CtClass</code> object from that array. To do this,
+use <code>ByteArrayClassPath</code>. For example,
+
+<ul><pre>
+ClassPool cp = ClassPool.getDefault();
+byte[] b = <em>a byte array</em>;
+String name = <em>class name</em>;
+cp.insertClassPath(new ByteArrayClassPath(name, b));
+CtClass cc = cp.get(name);
+</pre></ul>
+
+<p>The obtained <code>CtClass</code> object represents
+a class defined by the class file specified by <code>b</code>.
+
+
+<p>Since <code>ClassPath</code> is an interface, the users can define
+a new class implementing this interface and they can add an instance
+of that class so that a class file is obtained from a non-standard resource.
+
+<p><br>
+
+<a name="def">
+<h2>2. Defining a new class</h2>
+
+<p>To define a new class from scratch, <code>makeClass()</code>
+must be called on a <code>ClassPool</code>.
+
+<ul><pre>
+ClassPool pool = ClassPool.getDefault();
+CtClass cc = pool.makeClass("Point");
+</pre></ul>
+
+<p>This program defines a class <code>Point</code>
+including no members.
+
+<p>A new class can be also defined as a copy of an existing class.
+The program below does that:
+
+<ul><pre>
+ClassPool pool = ClassPool.getDefault();
+CtClass cc = pool.makeClass("Point");
+cc.setName("Pair");
+</pre></ul>
+
+<p>This program first obtains the <code>CtClass</code> object
+for class <code>Point</code>. Then it gives a new name <code>Pair</code>
+to that <code>CtClass</code> object.
+If <code>get("Point")</code> is called on the <code>ClassPool</code>
+object, then a class file <code>Point.class</code> is read again and
+a new <code>CtClass</code> object for class <code>Point</code> is constructed
+again.
+
+<ul><pre>
+ClassPool pool = ClassPool.getDefault();
+CtClass cc = pool.makeClass("Point");
+CtClass cc1 = pool.get("Point"); // cc1 is identical to cc.
+cc.setName("Pair");
+CtClass cc2 = pool.get("Pair"); // cc2 is identical to cc.
+CtClass cc3 = pool.get("Point"); // cc3 is not identical to cc.
+</pre></ul>
+
+<p><br>
+
+<a name="mod">
+<h2>3. Modifying a class at load time</h2>
+
+<p>If what classes are modified is known in advance,
+the easiest way for modifying the classes is as follows:
+
+<ul><li>1. Get a <code>CtClass</code> object by calling
+ <code>ClassPool.get()</code>,
+ <li>2. Modify it, and
+ <li>3. Call <code>ClassPool.write()</code> or <code>writeFile()</code>.
+</ul>
+
+<p>If whether a class is modified or not is determined at load time,
+the users can write an event listener so that it is notified
+when a class is loaded into the JVM.
+A class loader (<code>java.lang.ClassLoader</code>) working with
+Javassist must call <code>ClassPool.write()</code> for obtaining
+a class file. The users can write an event listener so that it is
+notified when the class loader calls <code>ClassPool.write()</code>.
+The event-listener class must implement the following interface:
+
+<ul><pre>public interface Translator {
+ public void start(ClassPool pool)
+ throws NotFoundException, CannotCompileException;
+ public void onWrite(ClassPool pool, String classname)
+ throws NotFoundException, CannotCompileException;
+}</pre></ul>
+
+<p>The method <code>start()</code> is called when this event listener
+is registered to a <code>ClassPool</code> object.
+The method <code>onWrite()</code> is called when <code>write()</code>
+(or similar methods) is called on the <code>ClassPool</code> object.
+The second parameter of <code>onWrite()</code> is the name of the class
+to be written out.
+
+<p>Note that <code>start()</code> or <code>onWrite()</code> do not have
+to call <code>write()</code> or <code>writeFile()</code>. For example,
+
+<ul><pre>public class MyAnotherTranslator implements Translator {
+ public void start(ClassPool pool)
+ throws NotFoundException, CannotCompileException {}
+ public void onWrite(ClassPool pool, String classname)
+ throws NotFoundException, CannotCompileException
+ {
+ CtClass cc = pool.get(classname);
+ cc.setModifiers(Modifier.PUBLIC);
+ }
+}</pre></ul>
+
+<p>All the classes written out by <code>write()</code> are made public
+just before their definitions are translated into an byte array.
+
+<p><center><img src="overview.gif" alt="overview"></center>
+
+<p>The two methods <code>start()</code> and <code>onWrite()</code>
+can modify not only a <code>CtClass</code> object specified by
+the given <code>classname</code> but also
+<em>any</em> <code>CtClass</code> objects contained
+in the given <code>ClassPool</code>.
+They can call <code>ClassPool.get()</code> for obtaining any
+<code>CtClass</code> object.
+If a modified <code>CtClass</code> object is not written out immediately,
+the modification is recorded until that object is written out.
+
+<p><center><img src="sequence.gif" alt="sequence diagram"></center>
+
+<p>To register an event listener to a <code>ClassPool</code>,
+it must be passed to a constructor of <code>ClassPool</code>.
+Only a single event listener can be registered.
+If more than one event listeners are needed, multiple
+<code>ClassPool</code>s should be connected to be a single
+stream. For example,
+
+<ul><pre>Translator t1 = new MyTranslator();
+ClassPool c1 = new ClassPool(t1);
+Translator t2 = new MyAnotherTranslator();
+ClassPool c2 = new ClassPool(c1, t2);</pre></ul>
+
+<p>This program connects two <code>ClassPool</code>s.
+If a class loader calls <code>write()</code> on <code>c2</code>,
+the specified class file is first modified by <code>t1</code> and
+then by <code>t2</code>. <code>write()</code> returns the resulting
+class file.
+
+First, <code>onWrite()</code> on <code>t1</code> is called since
+<code>c2</code> obtains a class file by calling <code>write()</code>
+on <code>c1</code>. Then <code>onWrite()</code> on <code>t2</code>
+is called. If <code>onWrite()</code> called on <code>t2</code>
+obtains a <code>CtClass</code> object from <code>c2</code>, that
+<code>CtClass</code> object represents the class file that
+<code>t1</code> has modified.
+
+<p><center><img src="two.gif" alt="two translators"></center>
+
+<p><br>
+
+<a name="load">
+<h2>4. Class loader</h2>
+
+<p>Javassist can be used with a class loader so that bytecode can be
+modified at load time. The users of Javassist can define their own
+version of class loader but they can also use a class loader provided
+by Javassist.
+
+<p><br>
+
+<h3>4.1 Using <code>javassist.Loader</code></h3>
+
+<p>Javassist provides a class loader
+<code>javassist.Loader</code>. This class loader uses a
+<code>javassist.ClassPool</code> object for reading a class file.
+
+<p>For example, <code>javassist.Loader</code> can be used for loading
+a particular class modified with Javassist.
+
+<ul><pre>
+import javassist.*;
+import test.Rectangle;
+
+public class Main {
+ public static void main(String[] args) throws Throwable {
+ ClassPool pool = ClassPool.getDefault();
+ Loader cl = new Loader(pool);
+
+ CtClass ct = pool.get("test.Rectangle");
+ ct.setSuperclass(pool.get("test.Point"));
+
+ Class c = cl.loadClass("test.Rectangle");
+ Object rect = c.newInstance();
+ :
+ }
+}
+</pre></ul>
+
+<p>This program modifies a class <code>test.Rectangle</code>. The
+superclass of <code>test.Rectangle</code> is set to a
+<code>test.Point</code> class. Then this program loads the modified
+class into the JVM, and creates a new instance of the
+<code>test.Rectangle</code> class.
+
+<p>The users can use a <code>javassist.Translator</code> object
+for modifying class files.
+Suppose that an instance of a class <code>MyTranslator</code>,
+which implements
+<code>javassist.Translator</code>, performs modification of class files.
+To run an application class <code>MyApp</code> with the
+<code>MyTranslator</code> object, write a main class:
+
+<ul><pre>
+import javassist.*;
+
+public class Main2 {
+ public static void main(String[] args) throws Throwable {
+ Translator t = new MyTranslator();
+ ClassPool pool = ClassPool.getDefault(t);
+ Loader cl = new Loader(pool);
+ cl.run("MyApp", args);
+ }
+}
+</pre></ul>
+
+<p>To run this program, do:
+
+<ul><pre>
+% java Main <i>arg1</i> <i>arg2</i>...
+</pre></ul>
+
+<p>The class <code>MyApp</code> and the other application classes
+are translated by <code>MyTranslator</code>.
+
+<p>Note that <em>application</em> classes like <code>MyApp</code> cannot
+access the <em>loader</em> classes such as <code>Main</code>,
+<code>MyTranslator</code> and <code>ClassPool</code> because they
+are loaded by different loaders. The application classes are loaded
+by <code>javassist.Loader</code> whereas the loader classes such as
+<code>Main</code> are by the default Java class loader.
+
+<p>In Java, for security reasons, a single class file may be loaded
+into the JVM by two distinct class loaders so that two different
+classes would be created. For example,
+
+<ul><pre>class Point {
+ int x, y;
+}
+
+class Box {
+ Point base;
+ Point getBase() { return base; }
+}
+
+class Window {
+ Point size;
+ Point getSize() { return size; }
+}</pre></ul>
+
+<p>Suppose that a class <code>Box</code> is loaded by a class loader
+<code>L1</code> while a class <code>Window</code> is loaded by a class
+loader <code>L2</code>. Then, the obejcts returned by
+<code>getBase()</code> and <code>getSize()</code> are not instances of
+the same class <code>Point</code>.
+<code>getBase()</code> returns an instance of the class <code>Point</code>
+loaded by <code>L1</code> whereas <code>getSize()</code> returns an
+instance of <code>Point</code> loaded by <code>L2</code>. The two versions
+of the class <code>Point</code> are distinct. They belong to different
+name spaces. For more details, see the following paper:
+
+<ul>Sheng Liang and Gilad Bracha,
+"Dynamic Class Loading in the Java Virtual Machine",
+<br><i>ACM OOPSLA'98</i>, pp.36-44, 1998.</ul>
+
+<p>To avoid this problem, the two class loaders <code>L1</code> and
+<code>L2</code> must delegate the loading operation of the class
+<code>Point</code> to another class loader, <code>L3</code>, which is
+a parent class loader of <code>L1</code> and <code>L2</code>.
+<code>delegateLoadingOf()</code> in <code>javassist.Loader</code>
+is a method for specifying what classes should be loaded by the
+parent loader.
+
+<p>If <code>L1</code> is the parent class loader of <code>L2</code>,
+that is, if <code>L1</code> loads the class of <code>L2</code>,
+then <code>L2</code> can delegate the loading operation of
+<code>Point</code> to <code>L1</code> for avoiding the problem above.
+However, this technique does not work in the case below:
+
+<ul><pre>class Point { // loaded by L1
+ Window win;
+ int x, y;
+}
+
+class Box { // loaded by L1
+ Point base;
+ Point getBase() { return base; }
+}
+
+class Window { // loaded by L2
+ Point size;
+ Point getSize() { size.win = this; return size; }
+}</pre></ul>
+
+<p>Since all the classes included in a class definition loaded by
+a class loader <code>L1</code> are also loaded by <code>L1</code>,
+the class of the field <code>win</code> in <code>Point</code> is
+now the class <code>Window</code> loaded by <code>L1</code>.
+Thus <code>size.win = this</code> in <code>getSize()</code> raises
+a runtime exception because of type mismatch; the type of
+<code>size.win</code> is the class <code>Point</code> loaded by
+<code>L1</code> whereas the type of <code>this</code> is the class
+<code>Point</code> loaded by <code>L2</code>.
+
+<p><br>
+
+<h3>4.2 Writing a class loader</h3>
+
+<p>A simple class loader using Javassist is as follows:
+
+<ul><pre>import javassist.*;
+
+public class SimpleLoader extends ClassLoader {
+ /* Call MyApp.main().
+ */
+ public static void main(String[] args) throws Throwable {
+ SimpleLoader s = new SimpleLoader();
+ Class c = s.loadClass("MyApp");
+ c.getDeclaredMethod("main", new Class[] { String[].class })
+ .invoke(null, new Object[] { args });
+ }
+
+ private ClassPool pool;
+
+ public SimpleLoader() throws NotFoundException {
+ pool = ClassPool.getDefault();
+ pool.insertClassPath("./class"); // <em>MyApp.class must be there.</em>
+ }
+
+ /* Finds a specified class.
+ * The bytecode for that class can be modified.
+ */
+ protected Class findClass(String name) throws ClassNotFoundException {
+ try {
+ CtClass cc = pool.get(name);
+ // <em>modify the CtClass object here</em>
+ byte[] b = pool.write(name);
+ return defineClass(name, b, 0, b.length);
+ } catch (NotFoundException e) {
+ throw new ClassNotFoundException();
+ } catch (IOException e) {
+ throw new ClassNotFoundException();
+ } catch (CannotCompileException e) {
+ throw new ClassNotFoundException();
+ }
+ }
+}</pre></ul>
+
+<p>The class <code>MyApp</code> is an application program.
+To execute this program, first put the class file under the
+<code>./class</code> directory, which must <em>not</em> be included
+in the class search path. The directory name is specified by
+<code>insertClassPath()</code> in the constructor.
+You can choose a different name instead of <code>./class</code> if you want.
+Then do as follows:
+
+<ul><code>% java SimpleLoader</code></ul>
+
+<p>The class loader loads the class <code>MyApp</code>
+(<code>./class/MyApp.class</code>) and calls
+<code>MyApp.main()</code> with the command line parameters.
+Note that <code>MyApp.class</code> must not be under the directory
+that the system class loader searches. Otherwise, the system class
+loader, which is the parent loader of <code>SimpleLoader</code>,
+loads the class <code>MyApp</code>.
+
+<p>This is the simplest way of using Javassist. However, if you write
+a more complex class loader, you may need detailed knowledge of
+Java's class loading mechanism. For example, the program above puts the
+<code>MyApp</code> class in a name space separated from the name space
+that the class <code>SimpleLoader</code> belongs to because the two
+classes are loaded by different class loaders.
+Hence, the
+<code>MyApp</code> class cannot directly access the class
+<code>SimpleLoader</code>.
+
+<p><br>
+
+<h3>4.3 Modifying a system class</h3>
+
+<p>The system classes like <code>java.lang.String</code> cannot be
+loaded by a class loader other than the system class loader.
+Therefore, <code>SimpleLoader</code> or <code>javassist.Loader</code>
+shown above cannot modify the system classes at loading time.
+
+<p>If your application needs to do that, the system classes must be
+<em>statically</em> modified. For example, the following program
+adds a new field <code>hiddenValue</code> to <code>java.lang.String</code>:
+
+<ul><pre>ClassPool pool = ClassPool.getDefault();
+CtClass cc = pool.get("java.lang.String");
+cc.addField(new CtField(CtClass.intType, "hiddenValue", cc));
+pool.writeFile("java.lang.String", ".");</pre></ul>
+
+<p>This program produces a file <code>"./java/lang/String.class"</code>.
+
+<p>To run your program <code>MyApp</code>
+with this modified <code>String</code> class, do as follows:
+
+<ul><pre>
+% java -Xbootclasspath/p:. MyApp <i>arg1</i> <i>arg2</i>...
+</pre></ul>
+
+<p>Suppose that the definition of <code>MyApp</code> is as follows:
+
+<ul><pre>public class MyApp {
+ public static void main(String[] args) throws Exception {
+ System.out.println(String.class.getField("hiddenValue").getName());
+ }
+}</pre></ul>
+
+<p>If the modified <code>String</code> class is correctly loaded,
+<code>MyApp</code> prints <code>hiddenValue</code>.
+
+<p><i>Note: Applications that use this technique for the purpose of
+overriding a system class in <code>rt.jar</code> should not be
+deployed as doing so would contravene the Java 2 Runtime Environment
+binary code license.</i>
+
+<p><br>
+
+<a href="tutorial2.html">Next page</a>
+
+<hr>
+Java(TM) is a trademark of Sun Microsystems, Inc.<br>
+Copyright (C) 2000-2002 by Shigeru Chiba, All rights reserved.
+</body>
+</html>
diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html
new file mode 100644
index 00000000..b76996ce
--- /dev/null
+++ b/tutorial/tutorial2.html
@@ -0,0 +1,1069 @@
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Javassist Tutorial</title>
+ <link rel="stylesheet" type="text/css" href="brown.css">
+</head>
+
+<body>
+
+<div align="right">Getting Started with Javassist</div>
+
+<div align="left"><a href="tutorial.html">Previous page</a></div>
+
+<a name="intro">
+<h2>5. Introspection and customization</h2>
+
+<p><code>CtClass</code> provides methods for introspection. The
+introspective ability of Javassist is compatible with that of
+the Java reflection API. <code>CtClass</code> provides
+<code>getName()</code>, <code>getSuperclass()</code>,
+<code>getMethods()</code>, and so on.
+<code>CtClass</code> also provides methods for modifying a class
+definition. It allows to add a new field, constructor, and method.
+Instrumenting a method body is also possible.
+
+<p><hr width="40%">
+
+<ul>
+Javassist does not allow to remove a method or field, but it allows
+to change the name. So if a method is not necessary any more, it should be
+renamed and changed to be a private method by calling
+<code>setName()</code>
+and <code>setModifiers()</code> declared in <code>CtMethod</code>.
+
+<p>Javassist does not allow to add an extra parameter to an existing
+method, either. Instead of doing that, a new method receiving the
+extra parameter as well as the other parameters should be added to the
+same class. For example, if you want to add an extra <code>int</code>
+parameter <code>newZ</code> to a method:
+
+<ul><pre>void move(int newX, int newY) { x = newX; y = newY; }</pre></ul>
+
+<p>in a <code>Point</code> class, then you should add the following
+method to the <code>Point</code> class:
+
+<ul><pre>void move(int newX, int newY, int newZ) {
+ // do what you want with newZ.
+ move(newX, newY);
+}</pre></ul>
+
+</ul>
+
+<p><hr width="40%">
+
+<p>Javassist also provides low-level API for directly editing a raw
+class file. For example, <code>getClassFile()</code> in
+<code>CtClass</code> returns a <code>ClassFile</code> object
+representing a raw class file. <code>getMethodInfo()</code> in
+<code>CtMethod</code> returns a <code>MethodInfo</code> object
+representing a <code>method_info</code> structure included in a class
+file. The low-level API uses the vocabulary from the Java Virtual
+machine specification. The users must have the knowledge about class
+files and bytecode. For more details, the users should see the
+<code>javassist.bytecode</code> package.
+
+<p><br>
+
+<h3>5.1 Inserting source text at the beginning/end of a method body</h3>
+
+<p><code>CtMethod</code> and <code>CtConstructor</code> provide
+methods <code>insertBefore()</code>, <code>insertAfter()</code>, and
+<code>addCatch()</code>. They are used for inserting a code fragment
+into the body of an existing method. The users can specify those code
+fragments with <em>source text</em> written in Java.
+Javassist includes a simple Java compiler for processing source
+text. It receives source text
+written in Java and compiles it into Java bytecode, which will be inserted
+into a method body.
+
+<p>The methods <code>insertBefore()</code>, <code>insertAfter()</code>, and
+<code>addCatch()</code> receives a <code>String</code> object representing
+a statement or a block. A statement is a single control structure like
+<code>if</code> and <code>while</code> or an expression ending with
+a semi colon (<code>;</code>). A block is a set of
+statements surrounded with braces <code>{}</code>.
+Hence each of the following lines is an example of valid statement or block:
+
+<ul><pre>System.out.println("Hello");
+{ System.out.println("Hello"); }
+if (i < 0) { i = -i; }
+</pre></ul>
+
+<p>The statement and the block can refer to fields and methods.
+However, they <em>cannot refer to local variables</em> declared in the
+method that they are inserted into.
+They can refer to the parameters
+to the method although they must use different names
+<code>$0</code>, <code>$1</code>, <code>$2</code>, ... described
+below. Declaring a local variable in the block is allowed.
+
+<!--
+<p><center><table border=8 cellspacing=0 bordercolor="#cfcfcf">
+<tr><td bgcolor="#cfcfcf">
+<b>Tip:</b>
+<br>&nbsp&nbsp&nbsp Local variables are not accessible.&nbsp&nbsp
+</td></tr>
+</table></center>
+-->
+
+<p>The <code>String</code> object passed to the methods
+<code>insertBefore()</code>, <code>insertAfter()</code>, and
+<code>addCatch()</code> are compiled by
+the compiler included in Javassist.
+Since the compiler supports language extensions,
+several identifiers starting with <code>$</code>
+have special meaning:
+
+<ul><table border=0>
+<tr>
+<td><code>$0</code>, <code>$1</code>, <code>$2</code>, ... &nbsp &nbsp</td>
+<td>Actual parameters</td>
+</tr>
+
+<tr>
+<td><code>$args</code></td>
+<td>An array of parameters.
+The type of <code>$args</code> is <code>Object[]</code>.
+</td>
+</tr>
+
+<tr>
+<td><code>$$</code></td>
+<td rowspan=2>All actual parameters.<br>
+For example, <code>m($$)</code> is equivalent to
+<code>m($1,$2,</code>...<code>)</code></td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr>
+<td><code>$cflow(</code>...<code>)</code></td>
+<td><code>cflow</code> variable</td>
+</tr>
+
+<tr>
+<td><code>$r</code></td>
+<td>The result type. It is used in a cast expression.</td>
+</tr>
+
+<tr>
+<td><code>$w</code></td>
+<td>The wrapper type. It is used in a cast expression.</td>
+</tr>
+
+<tr>
+<td><code>$_</code></td>
+<td>The resulting value</td>
+</tr>
+
+<tr>
+<td><code>$sig</code></td>
+<td>An array of <code>java.lang.Class</code> objects representing
+the formal parameter types.
+</td>
+</tr>
+
+<tr>
+<td><code>$type</code></td>
+<td>A <code>java.lang.Class</code> object representing
+the formal result type.</td>
+</tr>
+
+<tr>
+<td><code>$class</code></td>
+<td>A <code>java.lang.Class</code> object representing
+the class currently edited.</td>
+</tr>
+
+</table>
+</ul>
+
+<h4>$0, $1, $2, ...</h4>
+
+<p>The parameters passed to the methods <code>insertBefore()</code>,
+<code>insertAfter()</code>, and <code>addCatch()</code>
+are accessible with
+<code>$0</code>, <code>$1</code>, <code>$2</code>, ... instead of
+the original parameter names.
+<code>$1</code> represents the
+first parameter, <code>$2</code> represents the second parameter, and
+so on. The types of those variables are identical to the parameter
+types.
+<code>$0</code> is
+equivalent to <code>this</code>. If the method is static,
+<code>$0</code> is not available.
+
+<p>These variables are used as following. Suppose that a class
+<code>Point</code>:
+
+<pre><ul>class Point {
+ int x, y;
+ void move(int dx, int dy) { x += dx; y += dy; }
+}
+</ul></pre>
+
+<p>To print the values of <code>dx</code> and <code>dy</code>
+whenever the method <code>move()</code> is called, execute this
+program:
+
+<ul><pre>ClassPool pool = ClassPool.getDefault();
+CtClass cc = pool.get("Point");
+CtMethod m = cc.getDeclaredMethod("move");
+m.insertBefore("{ System.out.println($1); System.out.println($2); }");
+cc.writeFile();
+</pre></ul>
+
+<p>Note that the source text passed to <code>insertBefore()</code> is
+surrounded with braces <code>{}</code>.
+<code>insertBefore()</code> accepts only a single statement or a block
+surrounded with braces.
+
+<p>The definition of the class <code>Point</code> after the
+modification is like this:
+
+<pre><ul>class Point {
+ int x, y;
+ void move(int dx, int dy) {
+ { System.out.println(dx); System.out.println(dy); }
+ x += dx; y += dy;
+ }
+}
+</ul></pre>
+
+<p><code>$1</code> and <code>$2</code> are replaced with
+<code>dx</code> and <code>dy</code>, respectively.
+
+<p><code>$1</code>, <code>$2</code>, <code>$3</code> ... are
+updatable. If a new value is assigend to one of those variables,
+then the value of the parameter represented by that variable is
+also updated.
+
+
+<h4>$args</h4>
+
+<p>The variable <code>$args</code> represents an array of all the
+parameters. The type of that variable is an array of class
+<code>Object</code>. If a parameter type is a primitive type such as
+<code>int</code>, then the parameter value is converted into a wrapper
+object such as <code>java.lang.Integer</code> to store in
+<code>$args</code>. Thus, <code>$args[0]</code> is equivalent to
+<code>$1</code> unless the type of the first parameter is a primitive
+type. Note that <code>$args[0]</code> is not equivalent to
+<code>$0</code>; <code>$0</code> represents <code>this</code>.
+
+<p>If an array of <code>Object</code> is assigned to
+<code>$args</code>, then each element of that array is
+assigned to each parameter. If a parameter type is a primitive
+type, the type of the corresponding element must be a wrapper type.
+The value is converted from the wrapper type to the primitive type
+before it is assigned to the parameter.
+
+<h4>$$</h4>
+
+<p>The variable <code>$$</code> is abbreviation of a list of
+all the parameters separated by commas.
+For example, if the number of the parameters
+to method <code>move()</code> is three, then
+
+<ul><pre>move($$)</pre></ul>
+
+<p>is equivalent to this:
+
+<ul><pre>move($1, $2, $3)</pre></ul>
+
+<p>If <code>move()</code> does not take any parameters,
+then <code>move($$)</code> is
+equivalent to <code>move()</code>.
+
+<p><code>$$</code> can be used with another method.
+If you write an expression:
+
+<ul><pre>exMove($$, context)</pre></ul>
+
+<p>then this expression is equivalent to:
+
+<ul><pre>exMove($1, $2, $3, context)</pre></ul>
+
+<p>Note that <code>$$</code> enables generic notation of method call
+with respect to the number of parameters.
+It is typically used with <code>$proceed</code> shown later.
+
+<h4>$cflow</h4>
+
+<p><code>$cflow</code> means "control flow".
+This read-only variable returns the depth of the recursive calls
+to a specific method.
+
+<p>Suppose that the method shown below is represented by a
+<code>CtMethod</code> object <code>cm</code>:
+
+<ul><pre>int fact(int n) {
+ if (n <= 1)
+ return n;
+ else
+ return n * fact(n - 1);
+}</pre></ul>
+
+<p>To use <code>$cflow</code>, first declare that <code>$cflow</code>
+is used for monitoring calls to the method <code>fact()</code>:
+
+<ul><pre>CtMethod cm = ...;
+cm.useCflow("fact");</pre></ul>
+
+<p>The parameter to <code>useCflow()</code> is the identifier of the
+declared <code>$cflow</code> variable. Any valid Java name can be
+used as the identifier. Since the identifier can also include
+<code>.</code> (dot), for example, <code>"my.Test.fact"</code>
+is a valid identifier.
+
+<p>Then, <code>$cflow(fact)</code> represents the depth of the
+recursive calls to the method specified by <code>cm</code>. The value
+of <code>$cflow(fact)</code> is 0 (zero) when the method is
+first called whereas it is 1 when the method is recursively called
+within the method. For example,
+
+<ul><pre>
+cm.insertBefore("if ($cflow(fact) == 0)"
+ + " System.out.println(\"fact \" + $1);");
+</pre></ul>
+
+<p>translates the method <code>fact()</code> so that it shows the
+parameter. Since the value of <code>$cflow(fact)</code> is checked,
+the method <code>fact()</code> does not show the parameter if it is
+recursively called within <code>fact()</code>.
+
+<p>The value of <code>$cflow</code> is the number of stack frames
+associated with the specified method <code>cm</code>
+under the current topmost
+stack frame for the current thread. <code>$cflow</code> is also
+accessible within a method different from the specified method
+<code>cm</code>.
+
+<h4>$r</h4>
+
+<p><code>$r</code> represents the result type (return type) of the method.
+It must be used as the cast type in a cast expression.
+For example, this is a typical use:
+
+<ul><pre>Object result = ... ;
+$_ = ($r)result;</pre></ul>
+
+<p>If the result type is a primitive type, then <code>($r)</code>
+converts from the wrapper type to the primitive type.
+For example, if the result type is <code>int</code>, then
+<code>($r)</code> converts from <code>java.lang.Integer</code> to
+<code>int</code>.
+
+<p>If the result type is <code>void</code>, then
+<code>($r)</code> does not convert a type; it does nothing.
+Moreover, the soruce text can include a <code>return</code>
+statement with a resulting value:
+
+<ul><pre>return ($r)result;</pre></ul>
+
+<p>Here, <code>result</code> is some local variable.
+Since <code>($r)</code> is specified, the resulting value is
+discarded.
+This <code>return</code> statement is regarded as the equivalent
+of the <code>return</code> statement without a resulting value:
+
+<ul><pre>return;</pre></ul>
+
+<h4>$w</h4>
+
+<p><code>$w</code> represents a wrapper type.
+It must be used as the cast type in a cast expression.
+<code>($w)</code> converts from a primitive type to the corresponding
+wrapper type.
+
+The following code is an example:
+
+<ul><pre>Integer i = ($w)5;</pre></ul>
+
+<p>The selected wrapper type depends on the type of the expression
+following <code>($w)</code>. If the type of the expression is
+<code>double</code>, then the wrapper type is <code>java.lang.Double</code>.
+
+<p>If the type of the expression following <code>($w)</code> is not
+a primitive type, then <code>($w)</code> does nothing.
+
+<h4>$_</h4>
+
+<p><code>insertAfter()</code> in <code>CtMethod</code> and
+<code>CtConstructor</code> inserts the
+compiled code at the end of the method. In the statement given to
+<code>insertAfter()</code>, not only the variables shown above such as
+<code>$0</code>, <code>$1</code>, ... but also <code>$_</code> is
+available.
+
+<p>The variable <code>$_</code> represents the resulting value of the
+method. The type of that variable is the type of the result type (the
+return type) of the method. If the result type is <code>void</code>,
+then the type of <code>$_</code> is <code>Object</code> and the value
+of <code>$_</code> is <code>null</code>.
+
+<p>Although the compiled code inserted by <code>insertAfter()</code>
+is executed just before the control normally returns from the method,
+it can be also executed when an exception is thrown from the method.
+To execute it when an exception is thrown, the second parameter
+<code>asFinally</code> to <code>insertAfter()</code> must be
+<code>true</code>.
+
+<p>If an exception is thrown, the compiled code inserted by
+<code>insertAfter()</code> is executed as a <code>finally</code>
+clause. The value of <code>$_</code> is <code>0</code> or
+<code>null</code> in the compiled code. After the execution of the
+compiled code terminates, the exception originally thrown is re-thrown
+to the caller. Note that the value of <code>$_</code> is never thrown
+to the caller; it is rather discarded.
+
+<h4>$sig</h4>
+
+<p>The value of <code>$sig</code> is an array of
+<code>java.lang.Class</code> objects that represent the formal
+parameter types in declaration order.
+
+<h4>$type</h4>
+
+<p>The value of <code>$type</code> is an <code>java.lang.Class</code>
+object representing the formal type of the result value. This
+variable is available only in <code>insertAfter()</code> in
+<code>CtMethod</code> and <code>CtConstructor</code>.
+
+<h4>$class</h4>
+
+<p>The value of <code>$class</code> is an <code>java.lang.Class</code>
+object representing the class in which the edited method is declared.
+
+<h4>addCatch()</h4>
+
+<p><code>addCatch()</code> inserts a code fragment into a method body
+so that the code fragment is executed when the method body throws
+an exception and the control returns to the caller. In the source
+text representing the inserted code fragment, the exception value
+is referred to with the name specified by the third parameter to
+<code>addCatch()</code>.
+
+<p>For example, this program:
+
+<ul><pre>
+CtMethod m = ...;
+CtClass etype = ClassPool.getDefault().get("java.io.IOException");
+m.addCatch("{ System.out.println(e); throw e; }", etype, "e");
+</pre></ul>
+
+<p>translates the method body represented by <code>m</code> into
+something like this:
+
+<ul><pre>
+try {
+ <font face="serif"><em>the original method body</em></font>
+}
+catch (java.io.IOException e) {
+ System.out.println(e);
+ throw e;
+}
+</pre></ul>
+
+<p>Note that the inserted code fragment must end with a
+<code>throw</code> or <code>return</code> statement.
+
+<p><br>
+
+<h3>5.2 Modifying a method body</h3>
+
+<p><code>javassist.expr.ExprEditor</code> is a class
+for replacing an expression in a method body.
+The users can define a subclass of <code>ExprEditor</code>
+to specify how an expression is modified.
+
+<p>To run an <code>ExprEditor</code> object, the users must
+call <code>instrument()</code> in <code>CtMethod</code> or
+<code>CtClass</code>.
+
+For example,
+
+<ul><pre>
+CtMethod cm = ... ;
+cm.instrument(
+ new ExprEditor() {
+ public void edit(MethodCall m)
+ throws CannotCompileException
+ {
+ if (m.getClassName().equals("Point")
+ && m.getMethodName().equals("move"))
+ m.replace("{ $1 = 0; $_ = $proceed($$); }");
+ }
+ });
+</pre></ul>
+
+<p>searches the method body represented by <code>cm</code> and
+replaces all calls to <code>move()</code> in class <code>Point</code>
+with a block:
+
+<ul><pre>{ $1 = 0; $_ = $proceed($$); }
+</pre></ul>
+
+<p>so that the first parameter to <code>move()</code> is always 0.
+Note that the substituted code is not an expression but
+a statement or a block.
+
+<p>The method <code>instrument()</code> searches a method body.
+If it finds an expression such as a method call, field access, and object
+creation, then it calls <code>edit()</code> on the given
+<code>ExprEditor</code> object. The parameter to <code>edit()</code>
+is an object representing the found expression. The <code>edit()</code>
+method can inspect and replace the expression through that object.
+
+<p>Calling <code>replace()</code> on the parameter to <code>edit()</code>
+substitutes the given statement or block for the expression. If the given
+block is an empty block, that is, if <code>replace("{}")</code>
+is executed, then the expression is removed from the method body.
+
+If you want to insert a statement (or a block) before/after the
+expression, a block like the following should be passed to
+<code>replace()</code>:
+
+<ul><pre>
+{ <em>before-statements;</em>
+ $_ = $proceed($$);
+ <em>after-statements;</em> }
+</pre></ul>
+
+<p>whichever the expression is either a method call, field access,
+object creation, or others. The second statement could be:
+
+<ul><pre>$_ = $proceed();</pre></ul>
+
+<p>if the expression is read access, or
+
+<ul><pre>$proceed($$);</pre></ul>
+
+<p>if the expression is write access.
+
+<h4>javassist.expr.MethodCall</h4>
+
+<p>A <code>MethodCall</code> object represents a method call.
+The method <code>replace()</code> in
+<code>MethodCall</code> substitutes a statement or
+a block for the method call.
+It receives source text representing the substitued statement or
+block, in which the identifiers starting with <code>$</code>
+have special meaning as in the source text passed to
+<code>insertBefore()</code>.
+
+<ul><table border=0>
+<tr>
+<td><code>$0</code></td>
+<td rowspan=3>
+The target object of the method call.<br>
+This is not equivalent to <code>this</code>, which represents
+the caller-side <code>this</code> object.<br>
+<code>$0</code> is <code>null</code> if the method is static.
+</td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr>
+<td><code>$1</code>, <code>$2</code>, ... &nbsp &nbsp</td>
+<td>
+The parameters of the method call.
+</td>
+</tr>
+
+<tr><td>
+<code>$_</code></td>
+<td>The resulting value of the method call.</td>
+</tr>
+
+<tr><td><code>$r</code></td>
+<td>The result type of the method call.</td>
+</tr>
+
+<tr><td><code>$class</code> &nbsp &nbsp</td>
+<td>A <code>java.lang.Class</code> object representing
+the class declaring the method.
+</td>
+</tr>
+
+<tr><td><code>$sig</code> &nbsp &nbsp</td>
+<td>An array of <code>java.lang.Class</code> objects representing
+the formal parameter types.</td>
+</tr>
+
+<tr><td><code>$type</code> &nbsp &nbsp</td>
+<td>A <code>java.lang.Class</code> object representing
+the formal result type.</td>
+</tr>
+
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>
+<td>The name of the method originally called
+in the expression.</td>
+</tr>
+
+</table>
+</ul>
+
+<p>Here the method call means the one represented by the
+<code>MethodCall</code> object.
+
+<p>The other identifiers such as <code>$w</code>,
+<code>$args</code> and <code>$$</code>
+are also available.
+
+<p>Unless the result type of the method call is <code>void</code>,
+a value must be assigned to
+<code>$_</code> in the source text and the type of <code>$_</code>
+is the result type.
+If the result type is <code>void</code>, the type of <code>$_</code>
+is <code>Object</code> and the value assigned to <code>$_</code>
+is ignored.
+
+<p><code>$proceed</code> is not a <code>String</code> value but special
+syntax. It must be followed by an argument list surrounded by parentheses
+<code>( )</code>.
+
+<h4>javassist.expr.FieldAccess</h4>
+
+<p>A <code>FieldAccess</code> object represents field access.
+The method <code>edit()</code> in <code>ExprEditor</code>
+receive this object if field access is found.
+The method <code>replace()</code> in
+<code>FieldAccess</code> receives
+source text representing the substitued statement or
+block for the field access.
+
+In the source text, the identifiers starting with <code>$</code>
+have also special meaning:
+
+<ul><table border=0>
+<tr>
+<td><code>$0</code></td>
+<td rowspan=3>
+The object containing the field accessed by the expression.
+This is not equivalent to <code>this</code>.<br>
+<code>this</code> represents the object that the method including the
+expression is invoked on.<br>
+<code>$0</code> is <code>null</code> if the field is static.
+</td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr>
+<td><code>$1</code></td>
+<td rowspan=2>
+The value that would be stored in the field
+if the expression is write access.
+<br>Otherwise, <code>$1</code> is not available.
+</td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr>
+<td><code>$_</code></td>
+<td rowspan=2>
+The resulting value of the field access
+if the expression is read access.
+<br>Otherwise, the value stored in <code>$_</code> is discarded.
+</td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+<tr>
+<td><code>$r</code></td>
+<td rowspan=2>
+The type of the field if the expression is read access.
+<br>Otherwise, <code>$r</code> is <code>void</code>.
+</td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr><td><code>$class</code> &nbsp &nbsp</td>
+<td>A <code>java.lang.Class</code> object representing
+the class declaring the field.
+</td></tr>
+
+<tr><td><code>$type</code></td>
+<td>A <code>java.lang.Class</code> object representing
+the field type.</td>
+</tr>
+
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>
+<td>The name of a virtual method executing the original
+field access.
+.</td>
+</tr>
+
+</table>
+</ul>
+
+<p>The other identifiers such as <code>$w</code>,
+<code>$args</code> and <code>$$</code>
+are also available.
+
+<p>If the expression is read access, a value must be assigned to
+<code>$_</code> in the source text. The type of <code>$_</code>
+is the type of the field.
+
+<h4>javassist.expr.NewExpr</h4>
+
+<p>A <code>NewExpr</code> object represents object creation
+with the <code>new</code> operator.
+The method <code>edit()</code> in <code>ExprEditor</code>
+receive this object if object creation is found.
+The method <code>replace()</code> in
+<code>NewExpr</code> receives
+source text representing the substitued statement or
+block for the object creation.
+
+In the source text, the identifiers starting with <code>$</code>
+have also special meaning:
+
+<ul><table border=0>
+
+<tr>
+<td><code>$0</code></td>
+<td>
+<code>null</code>.
+</td>
+</tr>
+
+<tr>
+<td><code>$1</code>, <code>$2</code>, ... &nbsp &nbsp</td>
+<td>
+The parameters to the constructor.
+</td>
+</tr>
+
+<tr>
+<td><code>$_</code></td>
+<td rowspan=2>
+The resulting value of the object creation.
+<br>A newly created object must be stored in this variable.
+</td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr>
+<td><code>$r</code></td>
+<td>
+The type of the created object.
+</td>
+</tr>
+
+<tr><td><code>$class</code> &nbsp &nbsp</td>
+<td>A <code>java.lang.Class</code> object representing
+the class of the created object.
+</td></tr>
+
+<tr><td><code>$sig</code> &nbsp &nbsp</td>
+<td>An array of <code>java.lang.Class</code> objects representing
+the formal parameter types.</td>
+</tr>
+
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>
+<td>The name of a virtual method executing the original
+object creation.
+.</td>
+</tr>
+
+</table>
+</ul>
+
+<p>The other identifiers such as <code>$w</code>,
+<code>$args</code> and <code>$$</code>
+are also available.
+
+<h4>javassist.expr.Instanceof</h4>
+
+<p>A <code>Instanceof</code> object represents an <code>instanceof</code>
+expression.
+The method <code>edit()</code> in <code>ExprEditor</code>
+receive this object if an instanceof expression is found.
+The method <code>replace()</code> in
+<code>Instanceof</code> receives
+source text representing the substitued statement or
+block for the expression.
+
+In the source text, the identifiers starting with <code>$</code>
+have also special meaning:
+
+<ul><table border=0>
+
+<tr>
+<td><code>$0</code></td>
+<td>
+<code>null</code>.
+</td>
+</tr>
+
+<tr>
+<td><code>$1</code></td>
+<td>
+The value on the left hand side of the original
+<code>instanceof</code> operator.
+</td>
+</tr>
+
+<tr>
+<td><code>$_</code></td>
+<td>
+The resulting value of the expression.
+The type of <code>$_</code> is <code>boolean</code>.
+</td>
+</tr>
+
+<tr>
+<td><code>$r</code></td>
+<td>
+The type on the right hand side of the <code>instanceof</code> operator.
+</td>
+</tr>
+
+<tr><td><code>$type</code></td>
+<td>A <code>java.lang.Class</code> object representing
+the type on the right hand side of the <code>instanceof</code> operator.
+</td>
+</tr>
+
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>
+<td rowspan=4>The name of a virtual method executing the original
+<code>instanceof</code> expression.
+<br>It takes one parameter (the type is <code>java.lang.Object</code>)
+and returns true
+<br>if the parameter value is an instance of the type on the right
+hand side of
+<br>the original <code>instanceof</code> operator.
+Otherwise, it returns false.
+</td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+<tr><td>&nbsp</td></tr>
+<tr><td>&nbsp</td></tr>
+
+</table>
+</ul>
+
+<p>The other identifiers such as <code>$w</code>,
+<code>$args</code> and <code>$$</code>
+are also available.
+
+<h4>javassist.expr.Cast</h4>
+
+<p>A <code>Cast</code> object represents an expression for
+explicit type casting.
+The method <code>edit()</code> in <code>ExprEditor</code>
+receive this object if explicit type casting is found.
+The method <code>replace()</code> in
+<code>Cast</code> receives
+source text representing the substitued statement or
+block for the expression.
+
+In the source text, the identifiers starting with <code>$</code>
+have also special meaning:
+
+<ul><table border=0>
+
+<tr>
+<td><code>$0</code></td>
+<td>
+<code>null</code>.
+</td>
+</tr>
+
+<tr>
+<td><code>$1</code></td>
+<td>
+The value the type of which is explicitly cast.
+</td>
+</tr>
+
+<tr>
+<td><code>$_</code></td>
+<td rowspan=2>
+The resulting value of the expression.
+The type of <code>$_</code> is the same as the type
+<br>after the explicit casting, that is, the type surrounded
+by <code>( )</code>.
+</td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr>
+<td><code>$r</code></td>
+<td>the type after the explicit casting, or the type surrounded
+by <code>( )</code>.
+</td>
+</tr>
+
+<tr><td><code>$type</code></td>
+<td>A <code>java.lang.Class</code> object representing
+the same type as <code>$r</code>.
+</td>
+</tr>
+
+<tr><td><code>$proceed</code> &nbsp &nbsp</td>
+<td rowspan=3>The name of a virtual method executing the original
+type casting.
+<br>It takes one parameter of the type <code>java.lang.Object</code>
+and returns it after
+<br>the explicit type casting specified by the original expression.
+
+</td>
+</tr>
+
+<tr><td>&nbsp</td></tr>
+
+<tr><td>&nbsp</td></tr>
+
+</table>
+</ul>
+
+<p>The other identifiers such as <code>$w</code>,
+<code>$args</code> and <code>$$</code>
+are also available.
+
+<p><br>
+
+<h3>5.3 Adding a new method or field</h3>
+
+<p>Javassist allows the users to create a new method and constructor
+from scratch. <code>CtNewMethod</code>
+and <code>CtNewConstructor</code> provide several factory methods,
+which are static methods for creating <code>CtMethod</code> or
+<code>CtConstructor</code> objects.
+Especially, <code>make()</code> creates
+a <code>CtMethod</code> or <code>CtConstructor</code> object
+from the given source text.
+
+<p>For example, this program:
+
+<ul><pre>
+CtClass point = ClassPool.getDefault().get("Point");
+CtMethod m = CtNewMethod.make(
+ "public int xmove(int dx) { x += dx; }",
+ point);
+point.addMethod(m);
+</pre></ul>
+
+<p>adds a public method <code>xmove()</code> to class <code>Point</code>.
+In this example, <code>x</code> is a <code>int</code> field in
+the class <code>Point</code>.
+
+<p>The source text passed to <code>make()</code> can refer to
+<code>$proceed</code> if the target object and the target method name
+are also given to <code>make()</code>. For example,
+
+<ul><pre>
+CtClass point = ClassPool.getDefault().get("Point");
+CtMethod m = CtNewMethod.make(
+ "public int ymove(int dy) { $proceed(0, dy); }",
+ point, "this", "move");
+</pre></ul>
+
+<p>this program creates a method <code>ymove()</code> defined below:
+
+<ul><pre>
+public int ymove(int dy) { this.move(0, dy); }
+</pre></ul>
+
+<p>Note that <code>$proceed</code> has been replaced with
+<code>this.move</code>.
+
+<p>Javassist also allows the users to create a new field.
+
+<ul><pre>
+CtClass point = ClassPool.getDefault().get("Point");
+CtField f = new CtField(CtClass.intType, "z", point);
+point.addField(f);
+</pre></ul>
+
+<p>This program adds a field named <code>z</code> to class
+<code>Point</code>.
+
+<p>If the initial value of the added field must be specified,
+the program shown above must be modified into:
+
+<ul><pre>
+CtClass point = ClassPool.getDefault().get("Point");
+CtField f = new CtField(CtClass.intType, "z", point);
+point.addField(f, "0"); <em>// initial value is 0.</em>
+</pre></ul>
+
+<p>Now, the method <code>addField()</code> receives the second parameter,
+which is the source text representing an expression computing the initial
+value. This source text can be any Java expression if the result type
+of the expression matches the type of the field. Note that an expression
+does not end with a semi colon (<code>;</code>).
+
+<p><br>
+
+<h3>5.4 Limitations</h3>
+
+<p>In the current implementation, the Java compiler included in Javassist
+has several limitations with respect to the language that the compiler can
+accept. Those limitations are:
+
+<p><li>The <code>.class</code> notation is not supported. Use the
+method <code>Class.forName()</code>.
+In regular
+Java, an expression <code>Point.class</code> means a <code>Class</code>
+object representing the <code>Point</code> class. This notation is
+not available.
+
+<p><li>Array initializers, a comma-separated list of expressions
+enclosed by braces <code>{</code> and <code>}</code>, are not
+supported.
+
+<p><li>Inner classes or anonymous classes are not supported.
+
+<p><li><code>switch</code> statements are not supported yet.
+
+<p><li>Labeled <code>continue</code> and <code>break</code> statements
+are not supported.
+
+<p><li>The <code>finally</code> clause following
+<code>try</code> and <code>catch</code> clauses is not supported.
+
+<p><li>The compiler does not correctly implement the Java method dispatch
+algorithm. The compiler may confuse if methods defined in a class
+have the same name but take different parameter lists.
+
+<p><li>The users are recommended to use <code>#</code> as the separator
+between a class name and a static method or field name.
+For example, in regular Java,
+
+<ul><pre>javassist.CtClass.intType.getName()</pre></ul>
+
+<p>calls a method <code>getName()</code> on
+the object indicated by the static field <code>intType</code>
+in <code>javassist.CtClass</code>. In Javassist, the users can
+write the expression shown above but they are recommended to
+write:
+
+<ul><pre>javassist.CtClass#intType.getName()</pre></ul>
+
+<p>so that the compiler can quickly parse the expression.
+</ul>
+
+<p><br>
+
+<a href="tutorial.html">Previous page</a>
+
+<hr>
+Java(TM) is a trademark of Sun Microsystems, Inc.<br>
+Copyright (C) 2000-2002 by Shigeru Chiba, All rights reserved.
+</body>
+</html>
diff --git a/tutorial/two.gif b/tutorial/two.gif
new file mode 100644
index 00000000..ad6984c1
--- /dev/null
+++ b/tutorial/two.gif
Binary files differ